如何在asyncio.wait中捕获WebSocket断开异常以实现优雅清理
解决WebSocket客户端断开时的优雅异常处理问题
首先,咱们直接说核心问题:你不需要用asyncio.wait()包裹websocket.send(),反而这个操作把异常藏在了Future对象里,导致你没法直接捕获。websocket.send()本身就是一个协程,当客户端断开连接时,它会直接抛出ConnectionClosedError,咱们直接await它并捕获异常就好。
正确的实现代码
from websockets.exceptions import ConnectionClosedError async def status(websocket, message): try: print("\nDEBUG: "+message) await websocket.send(message) # 直接await发送协程,无需asyncio.wait except ConnectionClosedError: # 在这里执行你的优雅清理逻辑:比如标记客户端离线、释放资源等 print("客户端已异常断开,开始执行清理操作") raise Abort() # 如果你需要用自定义异常终止上层流程
为什么你的原代码会出问题?
原代码的问题:
你用await asyncio.wait([websocket.send(message)])时,asyncio.wait会将协程包装成Task,当发送操作抛出异常,这个异常会被存储在返回的Future中,而不是直接向上抛出,所以你的except块根本捕获不到它,最终导致Task exception was never retrieved的警告。修改后代码的问题:
你尝试通过Future获取异常,但判断条件写错了:future.exception() is websockets.ConnectionClosedError。这里future.exception()返回的是异常实例,而不是异常类,正确的判断应该是isinstance(future.exception(), websockets.ConnectionClosedError)。但其实完全没必要绕这个弯,直接await协程就能直接捕获异常。
额外说明
websocket.send()是协程函数,调用后返回协程对象,直接await它就能完成发送,同时连接断开时会直接抛出ConnectionClosedError,try-except块可以直接捕获。asyncio.wait()适用于同时等待多个异步任务完成的场景,单个发送任务完全不需要用它,反而会增加异常处理的复杂度。
内容的提问来源于stack exchange,提问作者Vishal DS
相关产品推荐
相关产品推荐

