How to know TCP connection is closed in net package?
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.
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 returnio.EOF
from zero byte reads
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"