Recv () hangs after remote host terminates

My problem is that I have a stream that is in the recv () call. The remote host terminates abruptly (without calling the socket close ()), and the call to recv () continues to block. This is clearly not good, because when I join threads to close the process (locally), this thread will never exit, because it is waiting on recv, which will never appear.

So my question is, what method do people consider the best way to deal with this problem? There are a few additional comments that should be known before answering:

  • I have no way to verify that the remote host is closing the socket before exiting.

  • This solution cannot use external libraries (e.g. boost). It should use standard C ++ / C libraries / functions (preferably not C ++ 0x).

I know this was most likely asked in the past, but id wanted someone to think about how to fix this problem correctly (without doing something super hacks that I would have done in the past).

Thanks!

+4
source share
4 answers

Assuming you want to continue using blocking sockets, you can use the SO_RCVTIMEO 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 start getting:

 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 want to use non-blocking I / O, then you can use poll() , select() , epoll() , kqueue() or any appropriate event dispatch mechanism for your system. The reason you need to use non-blocking I / O is because you have to let the recv() system call return to notify you that there is no data in the socket input queue. An example of use is a little more in demand:

 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; } 
+5
source

You can use TCP keep-alive probes to determine if a remote host is available. When keep-alive is on, the OS will send probes if the connection has been idle for too long; if the remote host does not respond to probes, the connection is closed.

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

Windows also uses the SO_KEEPALIVE option to enable keep-alive probes, but use the SIO_KEEPALIVE_VALS ioctl button to configure keep-alive parameters.

+1
source

You can 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 (readiness, readiness to write, or exception) in one or more file descriptors or timeout.

0
source

sockopts and select are probably the perfect choice. An additional option that you should consider as a backup is to send your process a signal (for example, by calling alarm() ). This should force any syscall in the process to exit and set errno to EINTR .

0
source

Source: https://habr.com/ru/post/1487402/


All Articles