套接字关闭后c ++ linux accept()阻塞

时间:2012-02-20 17:09:20

标签: c++ linux sockets blocking

我有一个侦听新连接的线程

new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size);

和另一个在关闭程序时关闭Listen_fd的线程。然而,在Listen_fd关闭后,它仍然会阻塞。当我使用GDB尝试和调试时,accept()不会阻塞。我认为这可能是SO_LINGER的问题,但默认情况下它不应该打开,并且在使用GDB时不应该更改。有什么想法,或关闭列表套接字的任何其他建议?

5 个答案:

答案 0 :(得分:6)

accept调用某些非有效套接字FD的行为未定义。 “不是有效的套接字FD”包括曾经是有效套接字但后来被关闭的数字。您可能会说“但Borealid,它应该返回EINVAL!”,但这并不能保证 - 例如,相同的FD号码可能会被重新分配到closeaccept个呼叫之间的不同套接字。 / p>

因此,即使您要隔离并纠正使程序失败的任何内容,您将来仍可能再次失败。不要这样做 - 纠正导致您尝试accept已关闭套接字上的连接的错误。

如果您的意思是accept 之前之前的呼叫在close之后继续阻止,那么您应该做的是发送一个发信号通知accept中被阻止的线程。这将给它EINTR并且它可以干净地脱离 - 然后然后关闭套接字。请勿使用非使用它的线程将其关闭。

答案 1 :(得分:5)

使用:sock.shutdown (socket.SHUT_RD)

然后accept将返回EINVAL。不需要丑陋的十字螺纹信号!

从Python文档: “注意close()会释放与连接关联的资源,但不一定会立即关闭连接。如果要及时关闭连接,请在shutdown()之前致电close()。” / p>

http://docs.python.org/3/library/socket.html#socket.socket.close

我在C编程时遇到了这个问题。但我今天才发现解决方案,在遇到Python中的同样问题后,并使用信号进行思考(哎呀!),然后记住关于{{的注释1}}!

至于说你不应该跨线程关闭/使用套接字的注释......在CPython中,全局解释器锁应该保护你(假设你使用的是文件对象而不是原始的整数文件描述符)。

以下是示例代码:

shutdown

答案 2 :(得分:1)

这是一种解决方法,但您可以在selectListen_fd超时,如果发生超时,请检查您是否要关闭该程序。如果是,退出循环,如果不是,请返回步骤1并执行下一个select

答案 3 :(得分:1)

您可能正在寻找shutdown()函数。调用shutdown(Listen_fd, SHUT_RDWR)将导致对accept()的任何阻止的调用都返回EINVAL。使用原子标记将对shutdown()的调用与耦合有助于确定EINVAL的原因。

例如,如果您具有以下标志:

std::atomic<bool> safe_shutdown(false);

然后您可以通过以下命令指示其他线程停止监听:

shutdown_handler([&]() {
  safe_shutdown = true;
  shutdown(Listen_fd, SHUT_RDWR);
});

为完整起见,这是您的线程如何调用accept:

while (true) {
  sockaddr_in clientAddr = {0};
  socklen_t clientAddrSize = sizeof(clientAddr);
  int connSd = accept(Listen_fd, (sockaddr *)&clientAddr, &clientAddrSize);
  if (connSd < 0) {
    // If shutdown_handler() was called, then exit gracefully
    if (errno == EINVAL && safe_shutdown)
      break;
    // Otherwise, it's an unrecoverable error
    std::terminate();
  }
  char clientname[1024];
  std::cout << "Connected to "
            << inet_ntop(AF_INET, &clientAddr.sin_addr, clientname,
                         sizeof(clientname))
            << std::endl;
  service_connection(connSd);
}

答案 4 :(得分:0)

你在检查关闭的返回值吗? 来自linux联机帮助页,(http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html) “关闭文件描述符可能是不明智的,因为它们可能在同一进程中的其他线程中被系统调用使用。由于文件描述符可能被重用,因此有一些模糊的竞争条件可能会导致意外的副作用”。 您可以使用select而不是accept并等待来自其他thead的某个事件,然后在侦听器线程中关闭套接字。