63

我正在实现一个小型 TCP 服务器。我如何知道我的一位客户是否已关闭?我应该尝试读取还是写入并检查是否err为零?

4

6 回答 6

75

该线程“可靠检测 TCP 连接已关闭的最佳方法”,使用net.Connfor ' c' (也见于utils/ping.golocale-backend/server.go许多其他实例):

one := make([]byte, 1)
c.SetReadDeadline(time.Now())
if _, err := c.Read(one); err == io.EOF {
  l.Printf(logger.LevelDebug, "%s detected closed LAN connection", id)
  c.Close()
  c = nil
} else {
  var zero time.Time
  c.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
}

为了检测超时,它建议:

if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
  ...

2019 年更新:tuxedo25在评论中提到:

在 go 1.7+ 中,零字节读取立即返回并且永远不会返回错误。
您必须至少读取一个字节。

请参阅提交 5bcdd63转到问题 15735

net:不要io.EOF从零字节读取返回

于 2012-10-05T07:22:27.847 回答
17

尝试读取它,如果它关闭它会抛出一个错误。如果您愿意,请优雅地处理!

对于放弃太多的风险:

func Read(c *net.Conn, buffer []byte) bool {
    bytesRead, err := c.Read(buffer)
    if err != nil {
        c.Close()
        log.Println(err)
        return false
    }
    log.Println("Read ", bytesRead, " bytes")
    return true
}

下面是使用 net 包制作小型 TCP“聊天服务器”的一个很好的介绍:

Golang Away:TCP 聊天服务器

于 2012-10-05T07:17:22.787 回答
4

在这方面挣扎了一段时间后,这是一个 POSIX 解决方案,用于MSG_PEEK防止耗尽缓冲区并导致竞争条件。这使您可以检查 TCP 套接字的 READ 部分是否仍然从另一个 goroutine 打开:

func connCheck(conn net.Conn) error {
    var sysErr error = nil
    rc, err := conn.(syscall.Conn).SyscallConn()
    if err != nil { return err }
    err = rc.Read(func(fd uintptr) bool {
        var buf []byte = []byte{0}
        n, _, err := syscall.Recvfrom(int(fd), buf, syscall.MSG_PEEK | syscall.MSG_DONTWAIT)
        switch {
        case n == 0 && err == nil:
            sysErr = io.EOF
        case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
            sysErr = nil
        default:
            sysErr = err
        }
        return true
    })
    if err != nil { return err }

    return sysErr
}

这是基于上面的 mysql#connCheck,但它执行 1 字节读取系统调用,这可能与尝试读取流的其他 goroutine 发生冲突。

于 2019-11-01T19:11:26.900 回答
2

2019年4月状态:

阅读https://github.com/golang/go/issues/15735上的线程和帖子

如果服务器没有在连接上写入(读取将失败,但写入不会),则普遍缺乏官方支持来检测服务器是否关闭了 TCP 连接。

https://github.com/methane提供了一个解决方案,该解决方案仅适用于 Linux,并且由于它进行一些分配而价格昂贵 -

这可以在这里找到:https ://github.com/go-sql-driver/mysql/blob/master/conncheck.go

我发现这是可行的,但不是跨平台阻止了我采用它-

于 2019-04-10T16:38:07.670 回答
-1

我的五美分,我认为最好写到连接并得到一个错误,感谢上面的答案,这是我的工作解决方案

func isTCPWorking(c net.Conn) bool {
   _, err := c.Write([]byte("OK"))
   if err != nil {
      c.Close() // close if problem
      return false
   }
   return true
}
于 2021-01-13T09:16:15.487 回答
-4
        _, err := conn.Read(make([]byte, 0))
        if err!=io.EOF{
            // this connection is invalid
            logger.W("conn closed....",err)

        }else{
            byt, _:= ioutil.ReadAll(conn);
        }
于 2016-10-17T08:58:19.273 回答