首页 文章

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

提问于
浏览
4

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

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

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

5 回答

  • 0

    accept 在调用不是有效套接字FD的内容时的行为未定义 . "Not a valid socket FD"包括曾经有效套接字但后来关闭的数字 . 您可能会说"but Borealid, it's supposed to return EINVAL!",但这并不能保证 - 例如,可能会将相同的FD号重新分配给 closeaccept 之间的不同套接字 .

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

    如果您的意思是之前在 close 之后对 accept continues 阻塞的呼叫,那么您应该做的是向在 accept 中被阻止的线程发送信号 . 这将给它EINTR并且它可以干净地脱离 - 并且 then 关闭套接字 . 请勿从使用它的线程以外的线程中关闭它 .

  • 5

    使用: sock.shutdown (socket.SHUT_RD)

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

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

    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

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

  • 6

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

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

相关问题