TCP is a good choice for communication if you want to know that your message got to where it was going, as opposed to UDP where the communication is fire-and-forget.
When a message is sent, the recipient sends an acknowledgement that the message arrived successfully. If you don’t get an acknowledgement, you have a pretty good idea that the message didn’t arrive. The protocol also has some other things built in to make sure that the messages arrive correctly and are assembled in the correct order – packets have a maximum size, so if you’re sending a larger overall message, the packets need to be stitched back together on the other side. It’ is possible for the packets to arrive out of order, so you need to know the order of the packets, too.
Half-Open Connections
Consider the scenario where the communication is, for the most part, one way. Recently I was connecting to a system that was sending back log and diagnostic information. I could send my own messages back, but for the most part the only reason you would connect is to view the current state / live diagnostics, not sending commands.
A half-open connection occurs when one end of the TCP connection fails, but does not properly notify the other side. Normally, if you want to end a connection, you would send a terminate signal to let the other side know that you’re not going to be transmitting or receiving any more information. There’s a couple of scenarios in which the connection can be severed without sending a termination command:
- Application / Computer crash
- Network failure – Router crash, cable unplugged, etc
If you try to send a message when the connection is in this state, you won’t get an acknowledgement back within the timeout and thus can detect that the connection failed. But, if your scenario is like mine above and you’re not sending data, you just won’t receive data from the other side. The problem arises if this could be expected (the data is only sent sporadically, so a sizeable gap between received messages is plausible).
Keep-Alive
Keep-Alive messages are small messages with no payload that are designed to keep the connection open and detect half-open connections. If you haven’t received a message for awhile, you can send a keep-alive packet just to ask the other side “hey, you still there?” Luckily, in C# at least, this mechanism is built-in. Unluckily, the timeout threshold is 2 hours, making it kind of impractical for many applications (such as my above example).
Configuring Keep-Alive
You can configure the keep-alive thresholds on a per-connection basis (note – its also possible to configure system-wide, but this requires changing a registry key and is generally discouraged). Unluckily, again, .NET didn’t really make it straightforward to do this… which is why I’m writing this blog post.
In order to set the keep-alive thresholds, you need to call IOControl on the socket, providing it with a byte array that matches this struct:
This struct is from the TCP/IP spec, but, confusingly, a u_long is only 4 bytes, which is not the same as a C# ulong which is 8 bytes. There’s other examples online showing how to pack these numbers in to a byte array and they all use uint. The only place I could find that definitively says that a u_long is 4 bytes is here, so I’m pretty sure it is.
There’s other ways to pack together things in to a byte array, but I find Buffer.BlockCopy to be pretty straightforward to understand.
SEE?? Straightforward!
A note about SetSocketOption
There’s another way to turn on keep-alive using SetSocketOption on the Socket object. NOTE, however, that this will not allow you to set the timeout thresholds, so you get stuck with the default of 2 hours.
What about Linux?
As of .NET Core 3.0, this is now supported in Linux.
Bob
Thank you for this Post. It sounds promising for me but there is one thing I don’t understand.
After configuring the timeout, how to I recognize that a timeout occurred?
I tried your code with the socket of a TcpClient, where I do read/write operations using the Networkstream of the object. But I don’t get a signal or exception when I Shutdown the server. So how can I check if die Connection was timed out?
Robert
Hey Bob,
I believe you should be seeing an IOException eventually. it wouldn’t happen right away – it depends on what value you have set for your read/write timeouts. Setting the keepalive will send a little ping message every so often to make sure the connection is still alive, so it still uses your write timeout when determining if the connection has failed or not. Hope this helps!
Oliver Schramm
Hey,
client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 2000);
should work cross-platform and send a KeepAlive packet every 2 seconds. There’s also SocketOptionName.TcpKeepAliveInterval which is the time the socket waits for an answer before another KeepAlive packet is sent.
Robert
Hey – when I wrote this post (in January 2019) it wasn’t supported, but support was added in .NET Core 3.0. I’ll update this post to reflect that.