简体   繁体   中英

Recv() call hangs after remote host terminates

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!

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;
}

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 .

You could use select()

From http://linux.die.net/man/2/select

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

select() blocks until the first event (read ready, write ready, or exception) on one or more file descriptors or a timeout occurs.

sockopts and select are probably the ideal choices. An additional option that you should consider as a backup is to send your process a signal (for example using the alarm() call). This should force any syscall in progress to exit and set errno to EINTR .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM