我相信 OP 的意图是从请求处理程序中关闭服务器,我认为他的KeyboardInterrupt
代码方面只是令人困惑。
CtrlC从运行服务器的 shell按下将成功关闭它,而无需执行任何特殊操作。您不能CtrlC从不同的外壳按下并期望它工作,我认为这个概念可能是这个令人困惑的代码的来源。没有必要像 OP 尝试的那样处理KeyboardInterrupt
in ,或者像另一个建议的那样处理。如果你都不做,它会按预期工作。handle()
serve_forever()
这里唯一的技巧——而且很棘手——是告诉服务器从处理程序中关闭而不会死锁。
正如 OP 在他的代码中解释和显示的那样,他使用的是单线程服务器,因此建议在“其他线程”中关闭它的用户没有注意。
我仔细研究了SocketServer
代码,发现BaseServer
该类在努力使用该模块中可用的线程混合时,实际上通过threading.Event
在serve_forever
.
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:
# XXX: Consider using another file descriptor or
# connecting to the socket to wake this up instead of
# polling. Polling reduces our responsiveness to a
# shutdown request and wastes cpu at all other times.
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