如何关闭Python的TCPServer、HTTPServer或SimpleHTTPServer?
我正在为一个C/C++的HTTP/HTTPS客户端搭建一个测试工具,我想用Python来做这个测试工具里的服务器。Python有一套很不错的TCP/HTTP服务器和处理类,感觉用它来完成这个任务会简单很多。
不过,我现在在想怎么优雅地关闭一个TCPServer
对象。我的测试工具是用C++写的,我想用popen()
来启动这个服务器,因为它比system()
更好用,而且在我所有的目标平台上(Windows、OSX、Linux)都能用。由于我用popen()
来启动Python服务器,我就拿不到Python进程的PID,所以不能直接用SIGTERM
来结束服务器。
我想给服务器进程的标准输入(STDIN)发送一个'q'字符,或者关闭服务器的标准输入管道,给它发送EOF,这样可以让它优雅地关闭。但我就是找不到一个好的方法来做到这一点。
TCPServer
/HTTPServer
等类在你调用它们的.serve_forever()
方法时开始工作。所以我想我不应该对它们在调用.serve_forever()
后一直服务而感到惊讶。作为替代,Python的BaseHTTPServer
文档建议可以反复调用.handle_request()
:
def run_while_true(server_class=BaseHTTPServer.HTTPServer,
handler_class=BaseHTTPServer.BaseHTTPRequestHandler):
"""
This assumes that keep_running() is a function of no arguments which
is tested initially and after each request. If its return value
is true, the server continues.
"""
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
while keep_running():
httpd.handle_request()
但这个方法也不太好用——它依赖于服务器接收到并完成一个请求才能检查是否继续运行。如果服务器没有收到请求,它就不会从.handle_request()
返回。
TCPServer
确实有一个叫.shutdown()
的方法:
BaseServer.shutdown()
Tell the serve_forever() loop to stop and wait until it does.
New in version 2.6.
这个方法是线程安全的吗?我知道有全局解释器锁(GIL),但我还是想知道在考虑到像PyPy这样的其他Python实现时,这是否被认为是正确的做法。我能否在另一个线程上等待标准输入,然后在收到EOF时安全地调用TCPServer
的.shutdown()
方法?
1 个回答
是的,BaseServer.shutdown()
方法是线程安全的。
serve_forever
方法会一直运行,直到设置了关闭标志,而设置这个标志的任务就是 shutdown()
方法。文档中提到:
这个方法会阻塞,直到循环结束。必须在
serve_forever()
在另一个线程中运行时调用,否则会导致死锁。
需要注意的是,这和全局解释器锁(GIL)没有关系;GIL 只是限制了 Python 进程在使用 C 扩展和输入输出时,能有效利用多少核心。