如何知道Golang网络包中的TCP连接是否已关闭?

时间:2012-10-05 07:14:01

标签: tcp go

我是Golang的新手。

我正在实施一个小型TCP服务器,如何知道我的某个客户是否已关闭?我应该尝试阅读或写入并检查错误是否为零?

6 个答案:

答案 0 :(得分:54)

该帖子“Best way to reliably detect that a TCP connection is closed”,使用net.Conn代表“c”(也见于utils/ping.golocale-backend/server.gomany other instances):

one := []byte{}
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提及in the comments

  

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

请参阅commit 5bcdd63go issue 15735

  

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

答案 1 :(得分:14)

只是尝试从中读取,如果关闭它将会抛出错误。如果你愿意,可以优雅地处理!

为了放弃太多的风险:

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
}

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

Golang Away: TCP Chat Server

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

经过一段时间的努力之后,这是一个POSIX解决方案,该解决方案使用MSG_PEEK来防止耗尽缓冲区并引起竞争状况。这使您可以检查是否从另一个goroutine中打开了TCP套接字的READ一半:

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字节的syscall,这可能与尝试读取流的其他goroutine冲突。

答案 4 :(得分:-1)

        _, err := conn.Read(make([]byte, 0))
        if err!=io.EOF{
            // this connection is invalid
            logger.W("conn closed....",err)

        }else{
            byt, _:= ioutil.ReadAll(conn);
        }

答案 5 :(得分:-1)

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

func isTCPWorking(c net.Conn) bool {
   _, err := c.Write([]byte("OK"))
   if err != nil {
      c.Close() // close if problem
      return false
   }
   return true
}