sockets - 如何在 Go 中停止监听服务器

标签 sockets networking go listen

我一直在尝试找到一种方法来优雅地停止 Go 中的监听服务器。因为 listen.Accept 阻塞,所以有必要关闭监听套接字以发出结束信号,但我无法将该错误与任何其他错误区分开来,因为未导出相关错误。

我能做得比这更好吗?请参阅 serve()

中以下代码中的 FIXME
package main

import (
    "io"
    "log"
    "net"
    "time"
)

// Echo server struct
type EchoServer struct {
    listen net.Listener
    done   chan bool
}

// Respond to incoming connection
//
// Write the address connected to then echo
func (es *EchoServer) respond(remote *net.TCPConn) {
    defer remote.Close()
    _, err := io.Copy(remote, remote)
    if err != nil {
        log.Printf("Error: %s", err)
    }
}

// Listen for incoming connections
func (es *EchoServer) serve() {
    for {
        conn, err := es.listen.Accept()
        // FIXME I'd like to detect "use of closed network connection" here
        // FIXME but it isn't exported from net
        if err != nil {
            log.Printf("Accept failed: %v", err)
            break
        }
        go es.respond(conn.(*net.TCPConn))
    }
    es.done <- true
}

// Stop the server by closing the listening listen
func (es *EchoServer) stop() {
    es.listen.Close()
    <-es.done
}

// Make a new echo server
func NewEchoServer(address string) *EchoServer {
    listen, err := net.Listen("tcp", address)
    if err != nil {
        log.Fatalf("Failed to open listening socket: %s", err)
    }
    es := &EchoServer{
        listen: listen,
        done:   make(chan bool),
    }
    go es.serve()
    return es
}

// Main
func main() {
    log.Println("Starting echo server")
    es := NewEchoServer("127.0.0.1:18081")
    // Run the server for 1 second
    time.Sleep(1 * time.Second)
    // Close the server
    log.Println("Stopping echo server")
    es.stop()
}

打印出来

2012/11/16 12:53:35 Starting echo server
2012/11/16 12:53:36 Stopping echo server
2012/11/16 12:53:36 Accept failed: accept tcp 127.0.0.1:18081: use of closed network connection

我想隐藏 Accept failed 消息,但显然我不想掩盖 Accept 可以报告的其他错误。我当然可以查看 use of closed network connection 的错误测试,但这真的很难看。我可以设置一个标志,说我即将关闭并忽略错误,如果设置了我想 - 有更好的方法吗?

最佳答案

我将通过使用 es.done 在关闭连接之前发送信号来处理这个问题。除了以下代码,您还需要使用 make(chan bool, 1) 创建 es.done,以便我们可以在其中放入单个值而不会阻塞。

// Listen for incoming connections
func (es *EchoServer) serve() {
  for {
    conn, err := es.listen.Accept()
    if err != nil {
      select {
      case <-es.done:
        // If we called stop() then there will be a value in es.done, so
        // we'll get here and we can exit without showing the error.
      default:
        log.Printf("Accept failed: %v", err)
      }
      return
    }
    go es.respond(conn.(*net.TCPConn))
  }
}

// Stop the server by closing the listening listen
func (es *EchoServer) stop() {
  es.done <- true   // We can advance past this because we gave it buffer of 1
  es.listen.Close() // Now it the Accept will have an error above
}

关于sockets - 如何在 Go 中停止监听服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13417095/

相关文章:

C# 已建立的连接被主机中的软件中止

c# - 实现 C# Tcp 打洞时出错 - 无法在 NAT 后面连接

linux - 我应该在哪里放置服务器的输入/输出控制台?

python - 测量服务器的 ping 延迟 - Python

go - 启动守护进程但直到守护进程启动进程在 golang 中完成后才继续

python - Python 中的数据包嗅探器

networking - 如何在 CoAP 一致性测试套件中对测试用例进行分组

go - 如何在自定义类型的 slice 上进行范围

go - 使用某些参数调用命令有效但不适用于其他参数但在控制台中有效

macos - 在Mac OS X(Darwin)下相当于AF_PACKET