我有一个侦听新连接的线程
new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size);
和另一个在关闭程序时关闭Listen_fd的线程。然而,在Listen_fd关闭后,它仍然会阻塞。当我使用GDB尝试和调试时,accept()不会阻塞。我认为这可能是SO_LINGER的问题,但默认情况下它不应该打开,并且在使用GDB时不应该更改。有什么想法,或关闭列表套接字的任何其他建议?
accept
在调用不是有效套接字FD的东西时的行为是未定义的。 “不是有效的套接字FD”包括曾经是有效套接字但后来被关闭的数字。您可能会说“但Borealid,它应该返回EINVAL!”,但这并不能保证 - 例如,相同的FD号可能会重新分配到close
和accept
调用之间的不同套接字。
因此,即使您要隔离并纠正使程序失败的任何原因,您将来仍可能再次失败。不要这样做 - 纠正导致您尝试在封闭的套接字上连接accept
的错误。
如果你的意思是之前对accept
进行的调用在close
之后继续阻塞,那么你应该做的是向在accept
中阻塞的线程发送一个信号。这将使它EINTR并且它可以干净地脱离 - 然后关闭插座。请勿从使用它的线程以外的线程中关闭它。
使用: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')
这是一种解决方法,但你可以在select
上使用Listen_fd
超时,如果发生超时,请检查你是否要关闭该程序。如果是这样,退出循环,如果没有,返回步骤1并执行下一个select
。
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);
}
你在检查关闭的返回值吗?来自linux手册页,(http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html)“关闭文件描述符可能是不明智的,因为系统可能正在使用它们在同一进程中调用其他线程。由于文件描述符可能被重用,因此存在一些模糊的竞争条件,可能会导致意外的副作用“。您可以使用select而不是accept并等待来自其他thead的某个事件,然后在侦听器线程中关闭套接字。