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

问题描述 投票:5回答:5

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

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

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

c++ linux sockets blocking
5个回答
6
投票

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

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

如果你的意思是之前对accept进行的调用在close之后继续阻塞,那么你应该做的是向在accept中阻塞的线程发送一个信号。这将使它EINTR并且它可以干净地脱离 - 然后关闭插座。请勿从使用它的线程以外的线程中关闭它。


5
投票

使用:sock.shutdown (socket.SHUT_RD)

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

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

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

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

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

这是示例代码:

import socket, threading, time

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind (('', 8000))
sock.listen (5)

def child ():
  print ('child  accept ...')
  try:  sock.accept ()
  except OSError as exc :  print ('child  exception  %s' % exc)
  print ('child  exit')

threading.Thread ( target = child ).start ()

time.sleep (1)
print ('main   shutdown')
sock.shutdown (socket.SHUT_RD)

time.sleep (1)
print ('main   close')
sock.close ()

time.sleep (1)
print ('main   exit')

1
投票

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


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

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.