Tuesday, 28 February 2017

Socket programming: when clients shut down

Have not been touching socket program for years. Recently wish to add log functions to libcsoup. The idea is transferring information to remote client, if there is. So the model is quite simple, I suppose, open a socket, listen on it, send to remote machine, if sending failed close the socket.

First problem is I have to open a thread to listen on the socket. The library doesn't have main thread so could not use select or poll. Signal model is even worse. The object structure has to be put in global to pass into the event handler.

The real trouble is when I use send() like this:

if (send(sockfd, buf, len, 0) < 0) {
    switch (errno) {
        case ENOTSOCK:...
            break;
        case EPIPE:...
            break;
    ...
}

The whole program exits instead of catching the error condition when the peer closes. Something deja vu so I did a quick search. The answer is SIGPIPE. When writing into a peer closed socket, the system send the SIGPIPE to the program. Apparently my sandbox doesn't prepare for the signal so the default behaviour is to exit the whole program.

Don't want to catch the signal because the program is part of a library. Reroute the signal process may cause other parts trouble. Read the man page of send() again. Found the last argument of send(),  flags, actually defined MSG_NOSIGNAL to avoid the signal:

MSG_NOSIGNAL (since Linux 2.2)
              Requests  not to send SIGPIPE on errors on stream oriented sockets when the other end breaks the
              connection.  The EPIPE error is still returned.

So change the code to:

if (send(sockfd, buf, len, MSG_NOSIGNAL) < 0) {
    switch (errno) {
        case ENOTSOCK:...
            break;
        case EPIPE:...
            break;
    ...
}

It works, hooray!


   

No comments:

Post a Comment