You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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")

核心要点说明

  1. 保存Server实例:websockets.serve返回的是协程,用run_until_complete执行后得到的Server对象才是关键——它提供了close()和wait_closed()方法,能优雅关闭现有连接、拒绝新连接。
  2. 线程安全操作事件循环:事件循环跑在自定义线程里,不能直接在主线程调用loop.stop(),必须用call_soon_threadsafe()把关闭任务提交到事件循环线程执行,避免线程冲突。
  3. 关闭顺序不能乱:先停新连接,再等现有连接处理完,最后停止事件循环——这个顺序能保证不丢数据、不残留僵尸连接。
  4. 处理协程取消:在listener里捕获CancelledError,确保服务器关闭时,正在处理的连接能干净退出,不会留下资源泄漏的隐患。

使用示例

# 启动服务器
server = WebSocketServer()
server.start()

# 模拟运行一段时间后停止
import time
time.sleep(10)
server.stop()

这样就能实现完全干净的停止,不会有残留线程或未关闭的连接了。

内容的提问来源于stack exchange,提问作者Notbad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:25:31