如何知道net包中的TCP连接已关闭?

huangapple go评论87阅读模式
英文:

How to know TCP connection is closed in net package?

问题

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

英文:

I'm implementing a small TCP server. How do I know if one of my clients closed? Should I just try to read or write and check if err is nil?

答案1

得分: 78

那个线程“可靠地检测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检测到关闭的局域网连接", 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+中,零字节读取会立即返回,永远不会返回错误。
您必须至少读取一个字节。

请参见提交5bcdd63go问题15735

英文:

That thread "Best way to reliably detect that a TCP connection is closed", using net.Conn for 'c' (also seen in utils/ping.go or locale-backend/server.go or many other instances):

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))
}

For detecting a timeout, it suggests:

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

Update 2019: tuxedo25 mentions in the comments:

> In go 1.7+, zero byte reads return immediately and will never return an error.
You must read at least one byte.

See commit 5bcdd63 and go issue 15735

> ## net: don't return io.EOF from zero byte reads

答案2

得分: 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 Chat Server"

英文:

Just try to read from it, and it will throw an error if it's closed. Handle gracefully if you wish!

For risk of giving away too much:

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
}

Here is a nice introduction to using the net package to make a small TCP "chat server":

"Golang Away: TCP Chat Server"

答案3

得分: 5

在这个问题上苦苦挣扎了一段时间后,这里有一个使用MSG_PEEK的POSIX解决方案,以防止排空缓冲区并引起竞争条件。这让你可以从另一个goroutine中检查TCP套接字的读取端口是否仍然打开:

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发生冲突。

英文:

After struggling for a while on this, here is a POSIX solution that uses MSG_PEEK to prevent draining the buffer and causing race conditions. This lets you check the whether the READ half of a TCP socket is still open from another 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
}

This is based on the above mysql#connCheck, but that one does a 1-byte read syscall, which can potentially conflict with other goroutines attempting to read a stream.

答案4

得分: 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

我发现这个解决方案有效,但由于不跨平台,无法采用。

英文:

April 2019 status:

Reading the threads and posts on https://github.com/golang/go/issues/15735

There is a general lack of official support to detect whether a server closed a TCP connection if the server does not write on the connection (reading will fail, but writing won't).

There is a solution provided by https://github.com/methane that works only on Linux and is expensive as it does some allocations -

This is available here: https://github.com/go-sql-driver/mysql/blob/master/conncheck.go

I found this to be working but not being cross platform is blocking me from adopting it -

答案5

得分: -3

我的五分钱,我觉得最好的办法是写入连接并获取错误,感谢上面的答案,这是我的工作解决方案

func isTCPWorking(c net.Conn) bool {
   _, err := c.Write([]byte("OK"))
   if err != nil {
	  c.Close() // 如果有问题就关闭连接
	  return false
   }
   return true
}
英文:

My five cents, I figured it is better to write to the connection and get an error, thanks for the answers above, here is my working solution

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

答案6

得分: -4

_, err := conn.Read(make([]byte, 0))
if err != io.EOF {
// 这个连接无效
logger.W("连接关闭....", err)
} else {
byt, _ := ioutil.ReadAll(conn)
}

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

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

huangapple
  • 本文由 发表于 2012年10月5日 15:14:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/12741386.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定