英文:
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.go
或locale-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() {
...
> 在go 1.7+中,零字节读取会立即返回,永远不会返回错误。
您必须至少读取一个字节。
英文:
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":
答案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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论