我有一个线程,用于监听新连接。
new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size);
还有一个线程,当关闭程序时会关闭Listen_fd。 然而,在关闭Listen_fd之后,它仍然阻塞。 当我使用GDB尝试调试accept()不阻塞。 我认为这可能是SO_LINGER的问题,但默认情况下不应该打开它,并且在使用GDB时不应更改它。 有任何想法是怎么回事,或者有任何其他建议用于关闭列表套接字吗?
我有一个线程,用于监听新连接。
new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size);
还有一个线程,当关闭程序时会关闭Listen_fd。 然而,在关闭Listen_fd之后,它仍然阻塞。 当我使用GDB尝试调试accept()不阻塞。 我认为这可能是SO_LINGER的问题,但默认情况下不应该打开它,并且在使用GDB时不应更改它。 有任何想法是怎么回事,或者有任何其他建议用于关闭列表套接字吗?
sock.shutdown(socket.SHUT_RD)
然后accept
将返回EINVAL
。不需要丑陋的跨线程信号!close()
释放与连接关联的资源,但不一定立即关闭连接。如果您想及时关闭连接,请在close()
之前调用shutdown()
。”
http://docs.python.org/3/library/socket.html#socket.socket.close
我多年前在使用C语言编程时遇到了这个问题,但是今天在使用Python时再次遇到同样的问题,考虑使用信号(呕吐!),然后想起了有关shutdown
的注释,终于找到了解决方案!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')
accept
时,其行为是未定义的。 "非有效的套接字FD"包括曾经有效但已被关闭的数字。你可能会说:"但是Borealid,它应该返回EINVAL!", 但这并不保证-例如,在您的close
和accept
调用之间,相同的FD号可能会重新分配给不同的套接字。
因此,即使您隔离并更正了导致尝试在关闭的套接字上进行accept
连接的错误,您仍然可能在将来再次失败。不要这样做-纠正导致您尝试在关闭的套接字上进行accept
连接的错误。
如果您的意思是先前对accept
的调用在close
之后继续阻塞,那么您应该向在accept
中阻塞的线程发送信号。这将为其提供EINTR,并且它可以干净地退出-然后关闭套接字。不要从使用它的线程以外的线程关闭它。
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);
}
这是一个解决方法,但你可以使用超时的方式在Listen_fd
上进行select
,如果发生超时,则检查是否要关闭程序。如果是,则退出循环,否则返回步骤1并执行下一个select
。
close(s)
无效,因为accept(…)
在关闭套接字后继续阻塞,但是shutdown(s, SHUT_RD)
会使accept(…)
立即返回错误码EINVAL
。这是完美的解决方案,即使在接受器线程在关闭发生时处于accept(…)
调用之间也可以正常工作。谢谢! - Matt Whitlock