How to close socket after tiny inactivity timeout?

Q. There is TCP connection, it needs to be closed and re initiated if there is no activity for a timeout period. The timeout period is tiny, of the order of few seconds or minutes?

Ans:
It is not easy to identify a dead or non responsive peer in TCP connection. Normally when the timeout period is long of the order of few hours, you can solve the problem using the TCP keepalive. But it makes no sense to use TCP keepalive when timeout period is tiny, of the order of few seconds. This tiny timeouts can be handled by select system call to check if there is any activity on the socket within the timeout period.

Lets say you’re listening on socket and you expect to read certain packet of known size at least once every 40 seconds, in case you don’t receive the packet you expect to close the TCP connection. You can solve this with following pseudo code.

int
readSocket(
    int fdSocket,
    char *buffer,
    int length
    )
{
    int totalRead;
    int thisTimeRead;
    fd_set readSet;
    int result;
    struct timeval timeout;

    for (totalRead = 0; totalRead < length; ) {
        FD_ZERO(&readSet);
        FD_SET(fdSocket, &readSet);
        timeout.tv_sec= 40;
        timeout.tv_usec = 0;
        /* wait till there is data to be read on the socket or timeout happens */
        result = select(fdSocket+1, &readSet, NULL, NULL, &timeout);
        if (-1 == result) {
            /* Some error in select api */
            if (EINTR == errno) {
                continue;
            }
            /* close socket and return error */
            close(fdSocket);
            return -1;
        }
        /* check if timeout happened */
        if (0 == result) {
            /* timeout, close the socket return error */
            close(fdSocket);
            return -1;
        }

        thisTimeRead = read(fdSocket, buffer+totalRead, length-totalRead);
        if (thisTimeRead < 0) {
            if (EINTR == errno) {
                continue;
            }
            return -1;
        } else if (0 == thisTimeRead) {
            /* EOF detected */
            break;
        }
        /* data is read from the socket, make the connection status active */
        totalRead += thisTimeRead;
    }

    return totalRead;
}

The preferred way to achieve the same effect for both socket read and write operations is by setting socket options: SO_RCVTIMEO and SO_SNDTIMEO

http://linux.die.net/man/7/socket

SO_RCVTIMEO and SO_SNDTIMEO
Specify the receiving or sending timeouts until reporting an error. The argument is a struct timeval. If an input or output function blocks for this period of time, and data has been sent or received, the return value of that function will be the amount of data transferred; if no data has been transferred and the timeout has been reached then -1 is returned with errno set to EAGAIN or EWOULDBLOCK, or EINPROGRESS (for connect(2)) just as if the socket was specified to be nonblocking. If the timeout is set to zero (the default) then the operation will never timeout. Timeouts only have effect for system calls that perform socket I/O (e.g., read(2), recvmsg(2), send(2), sendmsg(2)); timeouts have no effect for select(2), poll(2), epoll_wait(2), and so on.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

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