I'm writing a TCP client in Golang. The server will never send any replies, or any data.
As soon as I fail to write after a given timeout I want the client to close the connection.
So from reading the documentation for SetWriteDeadline
in Conn
: https://golang.org/pkg/net/#Conn
// SetWriteDeadline sets the deadline for future Write calls
// and any currently-blocked Write call.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
SetWriteDeadline(t time.Time) error
From the above description I use it like this:
...
for {
select {
case msg := <-messages:
conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
// When server goes away this just blocks forever. No timeout!
n, err := conn.Write(msg.ByteArray())
if err != nil {
return err
}
log.Printf("Sent %v bytes
", n)
}
}
...
But the timeout is never triggered if the server goes away, instead the Write
call will just block forever.
SetWriteDeadline
?SetWriteDeadline
is not the proper way to do that, how do I do it?This turned out to be a deadlock issue.
My program was using a lock each time it was sending on the messages
channel using the Fanout pattern.
The problem was that when I was returning the err
on the Write timeout there was a defer
call that attempted to lock that same Mutex
. Since I did not get the output I was expecting I thought the timeout never triggered.
defer func() {
thing.ThatCreatedDeadlock()
log.Println("Expected to see this")
}
So simply logging on the Write
error directly would have made this obvious:
n, err := conn.Write(msg.ByteArray())
if err != nil {
log.Println(err)
return err
}