Terminating a Connection

An application has several options for terminating a connection. The simplest is to call the closesocket() API, which takes only a socket descriptor as input. This API frees resources associated with a socket and initiates the graceful close sequence. This sequence is completed when the remote application also closes its socket.

If an application determines that it is done sending data, but may want to receive more data, it can call the shutdown() API. This API notifies the remote end that the local application won't be sending any more data, but may continue to receive data.

Lastly, an application may cause an "abortive" or "hard" close on a connection with the SO_LINGER socket option in conjunction with closesocket(). Setting the linger timeout to 0 causes the circuit to be terminated immediately, regardless of whether the remote end has completed its data transfer, and any unreceived or unsent data is dropped. Therefore, this option should be used with caution and only if the results are understood and intended. The following code fragment demonstrates how to perform a hard close on a connection:


LINGER         lingerInfo;
INT         err;
SOCKET         s;

/* First set the linger timeout on the socket to 0.  This will */
/* cause the connection to be reset. */

lingerInfo.l_onoff = 1;
lingerInfo.l_linger = 0;

setsockopt( s, SOL_SOCKET, SO_LINGER, (char *)&lingerInfo,sizeof(lingerInfo) );
closesocket(s);

How can an application know that the remote end has terminated the connection? The answer depends on whether the remote end terminated the connection gracefully or abortively. If the termination was abortive, then send() and recv() calls will fail with the error WSAECONNRESET. This indicates to an application that data may have been lost, and the error condition should be reported to the user.

If the termination was graceful, then any recv() calls made after all data has been received will return the value zero as the number of bytes received. This indicates that the remote end has gracefully terminated its end of the connection and the local end may close the socket without fear of data loss. The following code fragment illustrates how an application initiates a graceful close and then waits for the remote end to close gracefully before closing the socket.


SOCKET         s;
INT         err;
BYTE         buffer[1024];

err = shutdown( s , 1 );
if ( err == SOCKET_ERROR ) {
    sprintf(buf,"Windows Sockets error %d: Error during shutdown.",
         WSAGetLastError());
    MessageBox (hWnd,buf,"Windows Sockets Error",MB_OK);    
    shutdown_appl();    
}

/* Receive the rest of the pending data */

while ( (err = recv( s, buffer, sizeof(buffer), 0 ) != 0 ) { if ( err == SOCKET_ERROR ) { sprintf(buf,"Windows Sockets error %d: Error while receiving data.", WSAGetLastError()); MessageBox (hWnd,buf,"Windows Sockets Error",MB_OK); shutdown_appl(); } . . /* do something with the data we received. */ . . } /* The other side has also terminated. we can safely close now */ closesocket( s );

Note that in this example the application keeps calling recv() until it returns zero bytes received. If an application closes a socket and there is data available to be received, or data later arrives, then the system will abort the connection and throw out the data, since there is nobody to give the data to. Well-behaved applications should ensure that they receive all data before closing a socket.