6

我的问题是我有一个线程在 recv() 调用中。远程主机突然终止(没有 close() 套接字调用)并且 recv() 调用继续阻塞。这显然不好,因为当我加入线程以关闭进程(本地)时,该线程将永远不会退出,因为它正在等待一个永远不会到来的 recv。

所以我的问题是人们通常认为哪种方法是处理这个问题的最佳方法?在回答之前应该知道一些额外的注意事项:

  • 我无法确保远程主机在退出之前关闭套接字。

  • 此解决方案不能使用外部库(例如 boost)。它必须使用 C++/C 的标准库/特性(最好不是 C++0x 特定的)。

我知道这可能在过去被问过,但我想让别人知道如何正确地纠正这个问题(没有做一些我过去会做的超级骇人听闻的事情)。

谢谢!

4

4 回答 4

5

假设你想继续使用阻塞套接字,你可以使用SO_RCVTIMEOsocket 选项

   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.

因此,在您开始接收之前:

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 */

如果您愿意使用非阻塞 I/O,那么您可以使用poll()select()epoll()kqueue()或任何适合您系统的事件分派机制。您需要使用非阻塞 I/O 的原因是您需要允许系统调用recv()返回以通知您套接字的输入队列中没有数据。使用的示例涉及更多:

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;
}
于 2013-06-21T02:42:17.860 回答
2

您可以使用 TCP 保持活动探测来检测远程主机是否仍可访问。启用keep-alive后,如果连接空闲时间过长,操作系统会发送探测;如果远程主机不响应探测,则连接关闭。

在 Linux 上,您可以通过设置SO_KEEPALIVEsocket 选项来启用 keep-alive 探测,您可以使用 、 和 socket 选项配置 keep-aliveTCP_KEEPCNTTCP_KEEPIDLE参数TCP_KEEPINTVL。有关这些tcp(7)socket(7)更多信息,请参见。

Windows 还使用SO_KEEPALIVEsocket 选项来启用 keep-alive 探测,但要配置 keep-alive 参数,请使用SIO_KEEPALIVE_VALSioctl

于 2013-06-21T03:16:16.533 回答
0

你可以使用 select()

来自http://linux.die.net/man/2/select

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

select() 阻塞,直到一个或多个文件描述符上的第一个事件(读就绪、写就绪或异常)或发生超时。

于 2013-06-21T02:44:44.387 回答
0

sockopts 并且select可能是理想的选择。您应该考虑作为备份的另一个选项是向您的进程发送信号(例如使用alarm()调用)。这应该强制任何正在进行的系统调用退出并设置errnoEINTR.

于 2013-06-21T02:46:13.753 回答