Python3 Threaded SocketServer关闭时挂起问题求助
解决Python3 Threaded SocketServer关闭挂起的问题
我完全懂你遇到的糟心情况——用标准库socketserver搞多线程服务器,一开始关闭还正常,可telnet发几次命令后就卡在threading._wait_for_tstate_lock上,还想从请求处理器里触发关闭却找不到靠谱方案。其实问题根源很明确:ThreadingMixIn默认创建的是非守护线程,再加上你的handle方法没正确处理客户端断开,导致线程卡在循环里,服务器shutdown时要等这些线程结束才会退出,自然就挂住了。
下面给你一套完全基于标准库的Pythonic解决方案:
核心问题拆解
- 处理线程无法正常退出:你的
handle是while True循环,当客户端断开(比如telnet关闭),recv会返回空或抛出异常,但你用except Exception as e: pass吞了所有异常,线程会一直卡在循环里变成“僵尸线程”。 - 非守护线程阻塞shutdown:
ThreadingMixIn默认创建的是非守护线程,服务器调用shutdown()后会停接新连接,但会等所有非守护线程结束才真正退出,这些卡住的线程直接导致了挂起。
修复后的完整代码
# -*- coding: utf-8 -*- import socketserver import threading class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): # 设置处理线程为守护线程,主进程退出时自动终止这些线程 daemon_threads = True # 允许地址重用,避免重启服务器时出现"address already in use"错误 allow_reuse_address = True class ServiceHandler(socketserver.BaseRequestHandler): def handle(self): self.request.send(bytes("Welcome! Type 'shutdown' to stop the server.\n", "utf-8")) while True: try: msg = self.request.recv(1024).strip() if not msg: # 客户端正常断开(recv返回空),退出循环 break msg_str = str(msg, 'utf-8') if msg_str.lower() == 'shutdown': self.shutdown_server() break else: response = bytes(f"You said '{msg_str}'\n", "utf-8") self.request.send(response) except ConnectionResetError: # 客户端强制断开(比如telnet直接关闭),退出循环 break except Exception as e: # 打印异常方便调试,不再盲目吞掉 print(f"Request handling error: {e}") break def shutdown_server(self): self.request.send(bytes('Server shutdown requested. Closing connection...\n', 'utf-8')) self.request.close() # 直接通过handler的server属性访问服务器,不用全局变量 self.server.shutdown() print('Server shutdown initiated!') def run(): with ThreadedTCPServer(('', 1520), ServiceHandler) as server: # 启动服务器线程 server_thread = threading.Thread(target=server.serve_forever) server_thread.daemon = True server_thread.start() print(f"Server running on port 1520. Press Enter to shutdown...") input() # 关闭服务器 server.shutdown() print("Server closed successfully.") if __name__ == '__main__': run()
关键改动说明
开启
daemon_threads = True:
这是ThreadingMixIn自带的属性,设为True后所有处理客户端的线程都会变成守护线程。主进程准备退出时,不需要等待这些线程结束,直接终止它们,从根源避免挂起。修复
handle循环逻辑:- 检查
recv返回的msg是否为空,为空说明客户端正常断开,直接退出循环。 - 捕获
ConnectionResetError(客户端强制断开时抛出),及时退出循环。 - 不再吞掉所有异常,只处理客户端断开相关的异常,其他异常打印出来方便调试。
- 检查
从处理器触发关闭的正确姿势:
不用全局变量SERVER,BaseRequestHandler实例自带self.server属性,直接调用shutdown()就能触发服务器关闭,更符合面向对象的设计。用
with管理服务器:TCPServer实现了上下文管理器,with语句会自动调用server_close(),确保套接字等资源正确释放。
测试验证
- 启动服务器,用telnet连接:
telnet localhost 1520 - 发送几个普通命令,服务器正常响应。
- 发送
shutdown命令,服务器会关闭当前连接并触发全局shutdown,不会挂起。 - 直接按控制台Enter键,也能正常关闭服务器,完全解决之前的挂起问题。
这样修改后,你的服务器不管是从控制台触发关闭,还是从客户端命令触发,都能稳定退出,完全基于标准库实现,非常Pythonic。
内容的提问来源于stack exchange,提问作者moin moin
相关产品推荐
相关产品推荐

