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

liu*_*rry 48 tcp go

我是Golang的新手.

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

Von*_*onC 60

该线程" 的最佳方式可靠地检测TCP连接被关闭 ",使用net.Conn了" 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))
}
Run Code Online (Sandbox Code Playgroud)

为了检测超时,它建议:

if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
  ...
Run Code Online (Sandbox Code Playgroud)

  • 我不认为这确实有效.我已经尝试了这个答案提出的所有可能的组合,并且它从未检测到关闭的连接.我知道这一点,b/c我有这段代码,然后程序继续对连接和随后的Read()执行Write(),由于连接有错误而导致ErrUnexpectedEOF错误已经关闭. (6认同)
  • 如何使用零片大小?然后什么都不会被读取,我们只会检查超时.`zero:= make([] byte,0); ... c.读(零); ...` (4认同)
  • 在这个方法中,如果读取了字节,我要求将其放回。看来不可能。 (2认同)
  • 同样,无法让此代码在 EL6 go 1.4.1 上运行。但更改为“c.SetReadDeadline(time.Now())”为“c.SetReadDeadline(time.Now().Add(10 * time.Millisecond))”,它有效。 (2认同)

min*_*omi 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
}
Run Code Online (Sandbox Code Playgroud)

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

" Golang Away:TCP聊天服务器 "

  • 有很多可能的错误。只有其中之一意味着连接已被对等方关闭。 (3认同)

小智 6

经过一段时间的努力后,这里有一个 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
}
Run Code Online (Sandbox Code Playgroud)

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