Sep 29, 2011

Not Well Connected

Let me first say that I find .NET to be the best designed, easiest to use framework I've ever had the pleasure of dealing with in 30 years of programming. However, that doesn't mean they got everything right. The topic today is the Socket Connected property that lives in the System.Net.Sockets namespace.

You probably suspect that a property named Connected returns true if a socket is connected and returns false if it isn't connected. In fact it seems so obvious, that like me, you probably aren't too tempted to go read the docs on this property. That would be a mistake. A more accurate name for this property would be LastOperationSucceeded. Below is the relevant excerpt from the MSDN docs on the Connected property (emphasis is mine):

The Connected property gets the connection state of the Socket as of the last I/O operation. When it returns false, the Socket was either never connected, or is no longer connected.

The value of the Connected property reflects the state of the connection as of the most recent operation. If you need to determine the current state of the connection, make a non-blocking, zero-byte Send call.

I don't believe the docs are accurate. The Connected property returning false DOES NOT mean the socket is no longer connected. It just means the last operation failed. The difference is not pedantic and in fact designing code assuming Connected means there is a valid or invalid connection can easily lead to much confusion and heartache and ultimately a non-working design.

This became obvious to me because on the Server side of a connection (that sends data only) I wanted to know when the the client dropped the connection. The client drops the connection when it stops receiving data for a predefined amount of time. This means the last send by the server was a success so the server never knew the client was gone.

Equally important, on the server side I was checking Connected before doing a write to make sure the client was still connected. Big Mistake. In this application I shortened the send timeout to 50ms. If the client was too busy to get a frame of data it was no big deal I could just drop the packet on the floor.  The problem was that after dropping the packet Connected returns false. That means the server would quit sending data after the first dropped packet. The client is in fact still connected but since the last operation failed Connected returned false. 

The first problem  caused a hard to find problem when the next measurement started. For some reason if a client reestablished a connection (the old one was still valid) within 3 minutes all was well.  After three minutes the client connection would fail and the measurement would not work.

In my case since the client and server also exchange info through a WCF service my fix was to have the client directly inform the server about the end of the measurement, and force the server to disconnect. Now a client reconnecting always works. I also belatedly set the  ExclusiveAddressUse to true. It being false (the default) may have contributed to the strange three minute reconnect issue.

If you want to tell if you can still communicate  by doing a zero byte send here's a somewhat simplified version of the approach suggested from the above referenced MSDN docs. _serverTcpClient is the TcpClient field that was returned from the TcpListener call to TcpAcceptClient(). However, I don't even recommend this because this again, it isn't really telling you if the connection is valid, just if this operation worked.

ConnectionIsValid
  1. private bool ConnectionIsValid()
  2. {
  3.     if (_serverTcpClient == null) return false;
  4.  
  5.     Socket client = _serverTcpClient.Client;
  6.     bool blocking_state = client.Blocking;
  7.     bool is_connected = false;
  8.     try
  9.     {
  10.         byte[] tmp = new byte[1];
  11.         client.Blocking = false;
  12.         client.Send(tmp, 0, 0);
  13.        return  client.Connected;
  14.  
  15.     }
  16.     catch (SocketException e)
  17.     {
  18.         if (e.NativeErrorCode.Equals(10035)) //send would block
  19.           return true;
  20.         else
  21.         {
  22.           return false;
  23.         }
  24.     }
  25.     finally
  26.     {
  27.         client.Blocking = blocking_state;
  28.     }
  29. }

About Me

My photo
Tod Gentille (@todgentille) is now a Curriculum Director for Pluralsight. He's been programming professionally since well before you were born and was a software consultant for most of his career. He's also a father, husband, drummer, and windsurfer. He wants to be a guitar player but he just hasn't got the chops for it.