我认为OP的意图是从请求处理程序关闭服务器,我认为他代码中的KeyboardInterrupt部分只是让事情更加混乱。在运行服务器的shell中按下Ctrl + C将成功关闭它,不需要做任何特殊的操作。你不能从不同的shell中按下Ctrl + C并期望它能工作,我认为这个想法可能是这个令人困惑的代码的来源。没有必要在handle()中处理KeyboardInterrupt,就像OP尝试的那样,或者像其他人建议的那样,在serve_forever()周围进行处理。如果两者都不做,它的工作效果如预期一样。
这里唯一的技巧——而且它很棘手——是在处理程序中告诉服务器关闭而不死锁。正如OP解释并在他的代码中展示的那样,他正在使用单线程服务器,因此建议在“其他线程”中关闭服务器的用户没有注意到这一点。
我查看了SocketServer代码,并发现BaseServer类在努力与此模块中可用的线程mixin配合使用时,实际上使其更难与非线程服务器一起使用,通过在serve_forever中的循环周围使用threading.Event。
因此,我为单线程服务器编写了serve_forever的修改版本,使得可以从请求处理程序关闭服务器。
import SocketServer
import socket
import select
class TCPServerV4(SocketServer.TCPServer):
address_family = socket.AF_INET
allow_reuse_address = True
def __init__(self, server_address, RequestHandlerClass):
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
self._shutdown_request = False
def serve_forever(self, poll_interval=0.5):
"""provide an override that can be shutdown from a request handler.
The threading code in the BaseSocketServer class prevented this from working
even for a non-threaded blocking server.
"""
try:
while not self._shutdown_request:
r, w, e = SocketServer._eintr_retry(select.select, [self], [], [],
poll_interval)
if self in r:
self._handle_request_noblock()
finally:
self._shutdown_request = False
class TCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
data = self.request.recv(4096)
if data == "shutdown":
self.server._shutdown_request = True
host = 'localhost'
port = 52000
server = TCPServerV4((host, port), TCPHandler)
server.serve_forever()
如果您向服务器发送字符串
'shutdown'
,服务器将结束其
serve_forever
循环。您可以使用netcat进行测试:
printf "shutdown" | nc localhost 52000