WebSocket断开时websockets库主要抛ConnectionClosedError和ConnectionClosedOK异常,前者需重连,后者通常无需重连;此外还可能抛TimeoutError、OSError等,均应纳入重试兜底。

WebSocket断开时websockets库抛什么异常?
websockets在连接意外中断(如网络抖动、服务端关闭、超时)时,主要抛出ConnectionClosedError和ConnectionClosedOK。前者表示非预期断开,是重连的明确信号;后者代表服务端正常关闭,一般不需重连。实际中还会遇到TimeoutError(握手/读写超时)、OSError(底层socket错误)等,都应纳入重试兜底。
-
ConnectionClosedError:必须重连,常见于服务端崩溃或网络闪断 -
ConnectionClosedOK:检查code和reason,若为1000且无业务异常,可退出循环 -
TimeoutError或OSError:大概率是临时性故障,建议重试
用asyncio实现带退避的重连循环
手动轮询+指数退避是最可控的方式,避免依赖第三方库的黑盒逻辑。核心是把connect()包裹进while True,并在每次失败后等待递增延迟。
import asyncio
import websockets
import random
<p>async def connect_with_reconnect(uri, max_retries=5):
retry_count = 0
while retry_count <= max_retries:
try:
async with websockets.connect(uri, ping_interval=20, ping_timeout=10) as ws:</p><h1>连接成功,重置计数</h1><pre class='brush:python;toolbar:false;'> retry_count = 0
await handle_messages(ws) # 你的业务逻辑
break # 正常关闭则退出
except (websockets.exceptions.ConnectionClosedError,
websockets.exceptions.ConnectionClosedOK,
asyncio.TimeoutError,
OSError) as e:
retry_count += 1
if retry_count > max_retries:
raise e
# 指数退避 + 随机抖动,避免雪崩
delay = min(2 ** retry_count + random.uniform(0, 1), 60)
await asyncio.sleep(delay)-
ping_interval和ping_timeout能更快探测死连接,减少“假在线”时间 - 退避上限设为
60秒,防止无限拉长等待 - 抖动用
random.uniform(0, 1),避免多客户端同时重连冲击服务端
websockets重连时为什么消息会丢失?
WebSocket协议本身不保证断线期间的消息可靠投递。一旦连接断开,未发送完的send()会抛异常,已发但未确认的帧、服务端推送的中间消息,全部丢失。这不是重连逻辑的问题,而是协议限制。
- 客户端主动
send()失败时,必须在重连成功后重新构造并发送——你得自己缓存待发消息(例如用deque限长队列) - 服务端推送的消息无法回溯,除非它支持消息回放(如通过
last_message_id参数请求历史) - 不要依赖
ws.closed判断连接状态,它只反映最后一次操作结果;真正可靠的只有捕获异常或收到ping/pong超时
如何避免重连风暴和资源泄漏?
没加控制的重连可能瞬间创建大量连接尝试,耗尽本地端口或触发服务端限流。关键点在于节制并发、清理残留资源、区分错误类型。
立即学习“Python免费学习笔记(深入)”;
- 每次重连前检查
asyncio.current_task()是否被取消,及时退出 - 使用
asyncio.wait_for()包装connect(),防止单次连接卡死(如DNS阻塞) - 关闭连接时确保
ws.close()被调用,否则底层socket可能滞留TIME_WAIT状态 - 若使用
websockets.serve()做服务端,客户端重连频繁时注意max_connections配置,避免拒绝新连接
重连不是加个while True就完事,真正难的是判断“该不该连”“连之前有没有残留”“连上了消息怎么续”,这些细节不处理,线上跑几天就会暴露。


















