我正在编写要使用阻塞I / O的Python(v3.7.3)套接字服务器。我使用select()
并没有超时来接受新客户以及从中读取信息。我可以关闭监听套接字以中止select(),并捕获OSError作为停止执行的指示。
但是,当在单独的线程中运行时,这似乎不起作用,我也不明白为什么。
我了解还有其他方法可以完成此操作,例如使用超时,对select()使用虚拟套接字,或与侦听器建立虚拟连接以将其唤醒。但是所有这些在某种程度上都违反了使用select()的目的,并且在单线程中运行时并不必要。
这是重现此问题的基本示例,在我的实际代码中,它仅代表多个线程(因此,我首先使用线程):
#!/usr/bin/env python3
import signal
import socket
import threading
class SocketCloseTest:
"""Simple test case for using socket.close() to abort select.select()"""
def __init__(self,port,address=None):
self.port = port
self.address = address or ''
self.socket = None
def stop(self):
"""Close listening socket to stop select.select()"""
if self.socket:
print("Closing listener",self.socket)
self.socket.close()
print("Listener closed",self.socket)
def threaded_run(self):
"""Run test in a separate thread"""
thread = threading.Thread(target=self.run)
print("Starting sub-thread")
thread.start()
thread.join()
print("Sub-thread ended")
def run(self):
"""Run test"""
self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# Reuse port for quick re-launch of the application
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
self.socket.bind((self.address,self.port))
print("Starting listener")
self.socket.listen()
try:
print("select() started")
r,w,e = select.select([self.socket],[],[])
except OSError:
print("select() aborted")
else:
print("select() completed")
if __name__ == '__main__':
tester = SocketCloseTest(5000,address='')
# Set up signal handler for Ctrl-C
def signal_handler(signum,frame):
print("Received signal {}".format(signum))
tester.stop()
signal.signal(signal.SIGINT,signal_handler)
# This works
tester.run()
# This doesn't work
# tester.threaded_run()
print("Main thread ended")
使用test.run()
时,它会按预期运行并导致以下结果:
启动监听器
选择开始
^ C接收到的信号2
关闭监听器
侦听器关闭
选择已取消
主线程结束
但是,当与tester.threaded_run()
一起运行时,它只是挂在对select()的调用应中止的位置。奇怪的是,此时将作业放在后台会导致代码继续执行,应按以下步骤进行:
启动子线程
启动监听器
选择开始
^ C接收到的信号2
关闭监听器
侦听器关闭
-按下Ctrl-Z可以在Shell中挂起作业-$ bg
-壳牌在后台报告工作-
选择已取消
子线程已结束
主线程结束
谢谢...
- 编辑提到
accept()
患有完全相同的症状。