Tuesday, September 6, 2011

SO_LINGER example


Setting the SO_LINGER Option

Another commonly applied socket option is the SO_LINGER option. This option differs from the SO_REUSEADDR option in that the data  structure used is not a simple int data type. The purpose of the SO_LINGER option is to control how the socket is shut down when the function close(2) is called. This option applies only to connection-oriented protocols such as TCP.

The default behavior of the kernel is to allow the close(2) function to return immediately to the caller. Any unsent TCP/IP data will be transmitted and delivered if possible, but no guarantee is made. Because the close(2) call returns control immediately to the caller, the application has no way of knowing whether the last bit of data was actually delivered.

The SO_LINGER option can be enabled on the socket, to cause the application to block in the close(2) call until all final data is delivered to the remote end. Furthermore, this assures the
caller that both ends have acknowledged a normal socket shutdown. Failing this, the indicated option timeout occurs and an error is returned to the calling application.

One final scenario can be applied, by use of different SO_LINGER option values. If the calling application wants to abort communications immediately, appropriate values can be set in the
linger structure. Then, a call to close(2) will initiate an abort of the communication link, discarding all pending data and immediately close the socket.

The modes of operation for SO_LINGER are controlled by the structure linger:

struct linger {
   int l_onoff;
   int l_linger;
};

The member l_onoff acts as a Boolean value, where a nonzero value indicates TRUE and zero indicates FALSE. The three variations of this option are specified as follows:
  1. Setting l_onoff to FALSE causes member l_linger to be ignored and the default close (2) behavior implied. That is, the close(2) call will return immediately to the caller, and any pending data will be delivered if possible. 
  2. Setting l_onoff to TRUE causes the value of member l_linger to be significant. When l_linger is nonzero, this represents the time in seconds for the timeout period to be applied at close(2) time (the close(2) call will "linger"). If the pending data and successful close occur before the timeout occurs, a successful return takes place. Otherwise, an error return occur and errno is set to the value of EWOULDBLOCK. 
  3. Setting l_onoff to TRUE and setting l_linger to zero causes the connection to be aborted and any pending data is immediately discarded upon close(2).
You are probably well advised to write your applications so that the option SO_LINGER is enabled and a reasonable timeout is provided. Then, the return value from close(2) can be tested to see whether the connection was mutually shut down successfully. If an error is returned instead, this tells your application that it is probable that the remote application was unable to receive all the data that you sent. Alternatively, it might just mean that problems occurred when the connection was closed (after the data was successfully received by the peer).

You must be aware, however, that lingering in some server designs will create new problems. When the SO_LINGER option is configured to linger upon close(2), this will prevent other clients from being serviced while your server execution lingers within the close(2) function call. This problem exists if you are serving many clients within one process (usually a server that uses select(2) or poll(2)). Using the default behavior might be more appropriate because it will allow close(2) to return immediately. Any pending written data will still be delivered by the kernel, if it is able to.

Finally, using the abort behavior (mode number 3 listed previously) is appropriate if the application or server knows that the connection should be aborted. This might be applied when the server has determined that someone without access privilege is attempting to gain access. The client in this situation deserves no special care and so minimum overhead is expended in dispensing of the culprit.

The following shows an example of enabling the linger option, using a timeout (linger value) of 30 seconds:

Example

#define TRUE 1
#define FALSE 0

int z; /* Status code */
int s; /* Socket s */
struct linger so_linger;
. . .
so_linger.l_onoff = TRUE;
so_linger.l_linger = 30;
z = setsockopt(s,
               SOL_SOCKET,
               SO_LINGER,
               &so_linger,
               sizeof so_linger);
if ( z ) {
   perror("setsockopt(2)");
}

The next example shows how to establish SO_LINGER values to effect an abort of the current connection on socket s:

Example

#define TRUE 1
#define FALSE 0

int z; /* Status code */
int s; /* Socket s */
struct linger so_linger;
. . .
so_linger.l_onoff = TRUE;
so_linger.l_linger = 0;
z = setsockopt(s,
               SOL_SOCKET,
               SO_LINGER,
               &so_linger,
               sizeof so_linger);
if ( z ) {
   perror("setsockopt(2)");
}
close(s); /* Abort connection */

In the prior example, the socket connection s is aborted when the function close(2) is called. The abort semantic is implied by setting the timeout value to zero seconds.

See Also
SO_PASSCRED & SO_PEERCRED, SO_BROADCAST, SO_KEEPALIVE, SO_REUSEADD, SO_OBINLINE, SO_TYPE.

No comments:

Post a Comment