如何关闭Python的TCPServer、HTTPServer或SimpleHTTPServer?

3 投票
1 回答
2709 浏览
提问于 2025-04-18 09:49

我正在为一个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 个回答

2

是的,BaseServer.shutdown() 方法是线程安全的。

serve_forever 方法会一直运行,直到设置了关闭标志,而设置这个标志的任务就是 shutdown() 方法。文档中提到:

这个方法会阻塞,直到循环结束。必须在 serve_forever() 在另一个线程中运行时调用,否则会导致死锁。

需要注意的是,这和全局解释器锁(GIL)没有关系;GIL 只是限制了 Python 进程在使用 C 扩展和输入输出时,能有效利用多少核心。

撰写回答