Python中如何优雅关闭websockets库创建的WebSocket服务器
如何干净停止自定义线程中的Python websockets服务器?
我完全懂你的困扰——直接用run_forever()把线程死死卡住,想停的时候根本找不到发力点。核心问题在于你没保存实际的服务器实例,也没处理事件循环的优雅停止逻辑,下面一步步给你捋明白怎么解决:
关键问题拆解
你当前代码里的self._server只是websockets.serve返回的协程对象,不是真正运行的服务器实例。而且事件循环跑在自定义线程里,直接调用停止操作会有线程安全问题,必须用正确的姿势触发关闭流程。
完整解决方案代码
先给你改好的可运行框架,我会逐段解释核心逻辑:
import asyncio import websockets from threading import Thread # 替换成你实际使用的事件发射器库 from your_event_emitter import EventEmitter class WebSocketServer(EventEmitter): def __init__(self): super().__init__() self._loop = None self._server_instance = None # 保存实际运行的Server对象 self._server_thread = None def start(self): # 启动独立线程运行服务器 self._server_thread = Thread(target=self._run_server_loop, daemon=True) self._server_thread.start() def _run_server_loop(self): # 给线程创建专属的事件循环 self._loop = asyncio.new_event_loop() asyncio.set_event_loop(self._loop) async def client_listener(websocket, path): try: async for message in websocket: # 触发消息事件,耗时操作建议用asyncio.shield保护避免被取消 await asyncio.shield(self.onMessage.emit(message)) except asyncio.CancelledError: # 服务器关闭时协程会被取消,这里可以做资源清理 pass finally: # 确保连接正常关闭 await websocket.close() # 启动服务器并保存Server实例 start_server_coro = websockets.serve(client_listener, 'localhost', 8765) self._server_instance = self._loop.run_until_complete(start_server_coro) print("WebSocket server running on ws://localhost:8765") # 运行事件循环 self._loop.run_forever() print("Server event loop exited") def stop(self): if not self._loop or not self._loop.is_running(): print("Server isn't running, nothing to stop") return # 线程安全地将关闭任务提交到事件循环线程执行 def _cleanup(): # 1. 关闭服务器,停止接受新连接 self._server_instance.close() # 2. 等待所有现有连接处理完成并关闭 self._loop.run_until_complete(self._server_instance.wait_closed()) # 3. 停止事件循环 self._loop.stop() self._loop.call_soon_threadsafe(_cleanup) # 等待服务器线程结束 if self._server_thread: self._server_thread.join() print("WebSocket server stopped cleanly")
核心要点说明
- 保存Server实例:
websockets.serve返回的是协程,用run_until_complete执行后得到的Server对象才是关键——它提供了close()和wait_closed()方法,能优雅关闭现有连接、拒绝新连接。 - 线程安全操作事件循环:事件循环跑在自定义线程里,不能直接在主线程调用
loop.stop(),必须用call_soon_threadsafe()把关闭任务提交到事件循环线程执行,避免线程冲突。 - 关闭顺序不能乱:先停新连接,再等现有连接处理完,最后停止事件循环——这个顺序能保证不丢数据、不残留僵尸连接。
- 处理协程取消:在
listener里捕获CancelledError,确保服务器关闭时,正在处理的连接能干净退出,不会留下资源泄漏的隐患。
使用示例
# 启动服务器 server = WebSocketServer() server.start() # 模拟运行一段时间后停止 import time time.sleep(10) server.stop()
这样就能实现完全干净的停止,不会有残留线程或未关闭的连接了。
内容的提问来源于stack exchange,提问作者Notbad
相关产品推荐
相关产品推荐

