Thursday, April 10, 2008

Proper way to close TCP socket

This question arises very often in the developers communities.
For simplification I'll talk about synchronous sockets.

Generally, the procedure is like this:

  1. Finish sending data

  2. Call Socket.Shutdown with SocketShutdown.Send parameter

  3. Loop on Receive until it returns 0 or fails with an exception

  4. Call Close()

Here's a small sample in pseudo code that is very similar to C# :)

void CloseConnection(Socket socket)
{
socket.Send(/*last data of the connection*/);
socket.Shutdown(SocketShutdown.Send);

try
{
int read = 0;
while( (read = socket.Receive(/*application data buffers*/)) > 0 )
{}
}
catch
{
//ignore
}
socket.Close();
}

If first and third steps are skipped - data loss can happen.

Things become more complicated when using asynchronous sockets.
To prevent data loss while closing connection: termination logic can be added to the data exchange protocol.
For example, it can be some kind of termination message (depends on data protocol). When peer receives message of this kind, it can proceed with connection termination logic described above.

Another way is to put socket in a blocking mode (if socket was in non blocking mode) and close the connection in the way described above.

When designing network application one has to think also about its connection closing procedure. The purpose of proper connection close is prevention of data loss.

2 comments:

  1. Hi,

    First nice blog you have.

    I ran into some problems closing sockets properly and did some googling. After reading yours here I saw that at the server side after doing a shutdown(SD_SEND) it's absolutely necessary to try and read.

    What I do is :

    iResult =recv( conn_s, &ok , 1 , 0);
    int err = WSAGetLastError();
    if ( err == WSAECONNRESET )
    closesocket( conn_s );

    I try to read one single byte of the client but the client has already closed the connection so I get a CONNRESET error. This means that at server side I can close the socket too. This works perfectly.

    Before reading your post I sent one single back to the server meaning that the client had finished his tasks and that worked also.

    If I don't try to read at the server side using shutdown at both sides I always get connection resets when trying to read at the client. Apparently this is read is needed in conjunction with using shutdown. Previously I toughed that if you use shutdown at both sides the connection was automatically gracefully closed but this not case.

    Thanks for the ideas.

    ReplyDelete
  2. Shutdown can also be treated as some kind of "agreement" with lower socket layer. By calling shutdown you do not close connection but only prevent your layer from doing any I/O with the socket.

    The shutdown function disables sends or receives on a socket, but does not break the connection.

    ReplyDelete