I'm writing a simple TLV style service using TCP in go, that should be able to handle multiple connections, and allow for multiple messages to be sent on the same connection.
I need to be able to ensure that a message with incorrect length doesn't block the connection indefinitely, but I need to ensure that subsequent messages can be sent on the same connection without timing out.
I am using io.ReadFull
to read in a fixed number of bytes that contain the type and length, and then when I receive the length
, I am calling io.ReadFull
again and reading in length
number of bytes.
If I am reading in 4 bytes for the type and length, but the client only sends 3 for whatever reason, io.ReadFull
will hang. Similarly, if the client sends 300
for the length, but the length should only be 200
, io.ReadFull
will hang, blocking all communication on that channel.
I've tried using conn.SetReadDeadline()
and setting it to 5 seconds for this example. This causes the connection to timeout if the improper length is sent, which is great. The issue is that the connection will timeout if the next request isn't sent until after >5 seconds.
// ...
for {
conn, err := lis.Accept()
if err != nil {
fmt.Println(err)
continue
}
fmt.Println("Connected")
go handleC(conn)
}
func handleC(conn net.Conn) {
for {
err := conn.SetReadDeadline(time.Now().Add(5 * time.Second))
if err != nil {
fmt.Println(err)
break
}
l, err := readTL(conn)
if err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
fmt.Println("Timeout", err)
break
}
fmt.Println("Other error"), err
break
}
v, err := readV(conn, l)
if err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
fmt.Println("Timeout", err)
break
}
fmt.Println("Other error"), err
break
}
// go doStuffWithv()
}
}
func readTL(conn net.Conn) (l int, err error) {
buf := make([]byte, 4)
n, err := io.ReadFull(conn, buf)
if err != nil {
return l, err
}
fmt.Printf("Read %d bytes\n", n)
// getLengthFromTL()
return l, err
}
func readV(conn net.Conn, l int) (v []byte, err error) {
buf := make([]byte, l)
n, err := io.ReadFull(conn, buf)
if err != nil {
return v, err
}
fmt.Printf("Read %d bytes\n", n)
return v, err
}
If a client sends one request with the proper TL, things work as intended.
However, if the same client doesn't send a second message for 10 seconds, the connection will timeout before then, with the error tls: use of closed connection
Is there a way to ensure that this doesn't occur?
One thing I've tried doing is in the event of a timeout, it simply continues, rather than breaking.
I added in another error check to see if it is EOF
, and break if it is.
My first impression is that this works, but I'm not sure if there are instances where a connection timeout can mean that the connection is dead and shouldn't be used anymore or not, or if that would always return an EOF
error.