我有一个侦听新连接的线程
new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size);
和另一个在关闭程序时关闭Listen_fd的线程 . 然而,在Listen_fd关闭后,它仍然会阻塞 . 当我使用GDB尝试和调试时,accept()不会阻塞 . 我认为这可能是SO_LINGER的问题,但默认情况下它不应该打开,并且在使用GDB时不应该更改 . 有什么想法,或关闭列表套接字的任何其他建议?
accept 在调用不是有效套接字FD的内容时的行为未定义 . "Not a valid socket FD"包括曾经有效套接字但后来关闭的数字 . 您可能会说"but Borealid, it's supposed to return EINVAL!",但这并不能保证 - 例如,可能会将相同的FD号重新分配给 close 和 accept 之间的不同套接字 .
accept
close
因此,即使您要隔离并纠正使程序失败的任何原因,您将来仍可能再次失败 . 不要这样做 - 纠正导致您尝试在已关闭的套接字上 Build 连接的错误 .
如果您的意思是之前在 close 之后对 accept continues 阻塞的呼叫,那么您应该做的是向在 accept 中被阻止的线程发送信号 . 这将给它EINTR并且它可以干净地脱离 - 并且 then 关闭套接字 . 请勿从使用它的线程以外的线程中关闭它 .
使用: sock.shutdown (socket.SHUT_RD)
sock.shutdown (socket.SHUT_RD)
然后 accept 将返回 EINVAL . 不需要丑陋的十字螺纹信号!
EINVAL
从Python文档:“注意 close() 释放与连接关联的资源,但不一定立即关闭连接 . 如果要及时关闭连接,请在 close() 之前调用 shutdown() . ”
close()
shutdown()
http://docs.python.org/3/library/socket.html#socket.socket.close
几年前我遇到了这个问题,同时用C语言编程 . 但是我今天才发现解决方案,在Python中遇到同样的问题,并且使用信号进行思考(哎呀!),然后记住关于 shutdown 的注释!
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')
这是一种解决方法,但你可以在 Listen_fd 上超时,如果发生超时,请检查你是否要关闭该程序 . 如果是,退出循环,如果没有,返回步骤1并执行下一步 select .
Listen_fd
select
shutdown() 函数可能正是您要找的 . 调用 shutdown(Listen_fd, SHUT_RDWR) 将导致对 accept() 的任何阻止调用返回 EINVAL . 使用原子标志将 shutdown() 的调用耦合可以帮助确定 EINVAL 的原因 .
shutdown(Listen_fd, SHUT_RDWR)
accept()
例如,如果您有此标志:
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); }
你在检查关闭的返回值吗?来自linux手册页,(http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html)“关闭文件描述符可能是不明智的,因为系统可能正在使用它们在同一进程中调用其他线程 . 由于文件描述符可能被重用,因此存在一些模糊的竞争条件,可能会导致意外的副作用“ . 您可以使用select而不是accept并等待来自其他thead的某个事件,然后在侦听器线程中关闭套接字 .
5 回答
accept
在调用不是有效套接字FD的内容时的行为未定义 . "Not a valid socket FD"包括曾经有效套接字但后来关闭的数字 . 您可能会说"but Borealid, it's supposed to return EINVAL!",但这并不能保证 - 例如,可能会将相同的FD号重新分配给close
和accept
之间的不同套接字 .因此,即使您要隔离并纠正使程序失败的任何原因,您将来仍可能再次失败 . 不要这样做 - 纠正导致您尝试在已关闭的套接字上 Build 连接的错误 .
如果您的意思是之前在
close
之后对accept
continues 阻塞的呼叫,那么您应该做的是向在accept
中被阻止的线程发送信号 . 这将给它EINTR并且它可以干净地脱离 - 并且 then 关闭套接字 . 请勿从使用它的线程以外的线程中关闭它 .使用:
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中,全局解释器锁应该保护你(假设你使用的是文件对象而不是原始的整数文件描述符) .
这是示例代码:
这是一种解决方法,但你可以在
Listen_fd
上超时,如果发生超时,请检查你是否要关闭该程序 . 如果是,退出循环,如果没有,返回步骤1并执行下一步select
.shutdown()
函数可能正是您要找的 . 调用shutdown(Listen_fd, SHUT_RDWR)
将导致对accept()
的任何阻止调用返回EINVAL
. 使用原子标志将shutdown()
的调用耦合可以帮助确定EINVAL
的原因 .例如,如果您有此标志:
然后你可以通过以下方式指示其他线程停止监听:
为了完整性,以下是您的线程可以调用accept的方式:
你在检查关闭的返回值吗?来自linux手册页,(http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html)“关闭文件描述符可能是不明智的,因为系统可能正在使用它们在同一进程中调用其他线程 . 由于文件描述符可能被重用,因此存在一些模糊的竞争条件,可能会导致意外的副作用“ . 您可以使用select而不是accept并等待来自其他thead的某个事件,然后在侦听器线程中关闭套接字 .