Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recv() call hangs after remote host terminates

Tags:

c++

c

sockets

My problem is that I have a thread that is in a recv() call. The remote host suddenly terminates (without a close() socket call) and the recv() call continues to block. This is obviously not good because when I am joining the threads to close the process (locally) this thread will never exit because it is waiting on a recv that will never come.

So my question is what method do people generally consider to be the best way to deal with this issue? There are some additional things of note that should be known before answering:

  • There is no way for me to ensure that the remote host closes the socket prior to exit.

  • This solution cannot use external libraries (such as boost). It must use standard libraries/features of C++/C (preferably not C++0x specific).

I know this has likely been asked in the past but id like to get someones take as to how to correct this issue properly (without doing something super hacky which I would have done in the past).

Thanks!

like image 880
NothingMore Avatar asked Jun 21 '13 02:06

NothingMore


2 Answers

Assuming you want to continue to use blocking sockets, you can use the SO_RCVTIMEO socket option:

   SO_RCVTIMEO and SO_SNDTIMEO
          Specify the receiving or sending  timeouts  until  reporting  an
          error.   The parameter is a struct timeval.  If an input or out-
          put 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 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.

So, before you begin receiving:

struct timeval timeout = { timo_sec, timo_usec };
int r = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
assert(r == 0); /* or something more user friendly */

If you are willing to use non-blocking I/O, then you can use poll(), select(), epoll(), kqueue(), or whatever the appropriate event dispatching mechanism is for your system. The reason you need to use non-blocking I/O is that you need to allow the system call to recv() to return to notify you that there is no data in the socket's input queue. The example to use is a little bit more involved:

for (;;) {
    ssize_t bytes = recv(s, buf, sizeof(buf), MSG_DONTWAIT);
    if (bytes > 0) { /* ... */ continue; }
    if (bytes < 0) {
        if (errno == EWOULDBLOCK) {
            struct pollfd p = { s, POLLIN, 0 };
            int r = poll(&p, 1, timo_msec);
            if (r == 1) continue;
            if (r == 0) {
                /*...handle timeout */
                /* either continue or break, depending on policy */
            }
        }
        /* ...handle errors */
        break;
    }
    /* connection is closed */
    break;
}
like image 74
jxh Avatar answered Sep 18 '22 04:09

jxh


You can use TCP keep-alive probes to detect if the remote host is still reachable. When keep-alive is enabled, the OS will send probes if the connection has been idle for too long; if the remote host doesn't respond to the probes, then the connection is closed.

On Linux, you can enable keep-alive probes by setting the SO_KEEPALIVE socket option, and you can configure the parameters of the keep-alive with the TCP_KEEPCNT, TCP_KEEPIDLE, and TCP_KEEPINTVL socket options. See tcp(7) and socket(7) for more info on those.

Windows also uses the SO_KEEPALIVE socket option for enabling keep-alive probes, but for configuring the keep-alive parameters, use the SIO_KEEPALIVE_VALS ioctl.

like image 33
Adam Rosenfield Avatar answered Sep 18 '22 04:09

Adam Rosenfield