.NET Socket Implementation, Asynchronous Receiving and FIN/ACK

Recently I had to implement a persistent Socket implementation for a client. An issue appeared when a disconnect attempt is made by the remote server.

The TCP specification describes a “four-way close”. The disconnecting party issues a blank packet with the FIN and ACK flags set in the TCP header. The other party immediately responds with an empty packet containing an ACK flag in the TCP header, indicating receipt of the FIN/ACK packet. At this stage the other party is supposed to signal its readiness to terminate with another FIN/ACK back to the disconnecting party which is responded to with a single ACK. At this point the connection is considered severed.

The problem occurs with the socket when it’s configured to receive packets asynchronously. Mysteriously, the second FIN/ACK was not being transmitted back to the disconnecting party. Not the end of the world, but annoying none the less.

Here’s a sample of the asynchronous receive method. It’s static to make it thread-safe and receives state in a custom passed parameter object.

private static void ReceiveCallback( IAsyncResult ar )
{
  try
  {
    // Retrieve the state object and the client socket
    // from the asynchronous state object.
    StateObject state = (StateObject) ar.AsyncState;
    Socket client = state.workSocket;

    // Read data from the remote device.
    int bytesRead = client.EndReceive(ar);

    if (bytesRead > 0)
    {
      // There might be more data, so store the data received so far.
      state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
    }

    // Get the rest of the data.
    client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
      new AsyncCallback(ReceiveCallback), state);
   } catch (Exception e) {
    Console.WriteLine(e.ToString());
  }
}

When I debugged the transaction with a small test loopback socket I created I tracked the issue down to how the socket handles receiving the initial FIN/ACK supplied by the disconnecting party (my test loopback socket). The blank packet with the FIN/ACK set in the TCP header triggers the Asynchronous call back method itself. The socket does not interpret the FIN/ACK as a disconnect request at all. It’s up to asynchronous method implementation to handle receiving the FIN/ACK and either gracefully close the socket or signal its closure to another thread.

It’s pretty easy to determine if a FIN/ACK has been transmitted as no data will be received. The following implementation shuts down the socket gracefully when a FIN/ACK is received from a remote endpoint.

private static void ReceiveCallback( IAsyncResult ar )
{
  try
  {
    // Retrieve the state object and the client socket
    // from the asynchronous state object.
    StateObject state = (StateObject) ar.AsyncState;
    Socket client = state.workSocket;

    // Read data from the remote device.
    int bytesRead = client.EndReceive(ar);

    if (bytesRead > 0)
    {
      // There might be more data, so store the data received so far.
      state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
      // Get the rest of the data.
      client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
        new AsyncCallback(ReceiveCallback), state);
    }
    else
    {
      Client.Shutdown(SocketShutdown.Both);
      client.Close();
    }

   } catch (Exception e) {
    Console.WriteLine(e.ToString());
  }
}

Notice the use of the Shutdown method on the socket to ensure a more graceful disconnect. This would be so much nicer if it was possible to access the TCP headers present in the received packet but I haven’t been able to find a method to do that.
[tags].NET,Socket,FIN/ACK,TCP[/tags]

  1. Leave a comment

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: