服务端无ping间隔配置时WebSocket自动断开,客户端如何无需ping保活
问题根因
你遇到的InvalidStateError报错核心原因是没有对WebSocket连接的状态做校验,在send_stuff方法中直接调用send()时,连接可能还处于CONNECTING握手状态,或者已经因为空闲超时被中间节点/浏览器关闭,此时连接对象不可用就会抛出该错误。
就算服务端关闭了ping/pong校验,运营商、反向代理、网关等中间节点也会自动断开长时间无任何数据传输的TCP连接,这和WebSocket本身的协议校验无关,是传输层的通用规则,所以必须要有周期性的心跳包维持连接的活跃状态。
无需客户端发送ping的保活方案
完全可以通过服务端主动发送ping帧实现,不需要客户端写任何额外的心跳逻辑:
Python的websockets库原生支持服务端自动发送ping,你只需要把start_server方法中ping_interval=None改成正整数即可,单位为秒,修改后的服务端代码如下:
import asyncio import websockets import logging logger = logging.getLogger('websockets') logger.setLevel(logging.INFO) logger.addHandler(logging.StreamHandler()) class Main: def __init__(self): self.host = '127.0.0.1' self.port = 50223 async def check_declaration(self,websocket,path): while True: data = await websocket.recv() print(data) def start_server(self): loop = asyncio.get_event_loop() loop.run_until_complete( # 每30秒给客户端发一次ping,60秒没收到pong就判定连接断开 websockets.serve(self.check_declaration,self.host,self.port,ping_interval=30, ping_timeout=60)) loop.run_forever() Main().start_server()
浏览器作为标准WebSocket实现,收到服务端的ping帧后会自动回复pong帧,全程不需要客户端做任何开发,即可维持连接存活。
客户端代码修复
你的Bare类实现存在状态管理漏洞,需要补充连接状态监听和校验逻辑,修复后代码如下:
export class Bare{ // 存储连接实例和就绪状态 connections : Map<string,{ws:WebSocket, isReady:boolean}> constructor(){ this.connections = new Map(); } setup_connection(name:string){ const connection = new WebSocket('ws://localhost:50223'); this.connections.set(name,{ ws: connection, isReady: false }); // 连接建立完成后标记为可用 connection.onopen = () => { const conn = this.connections.get(name); if(conn) conn.isReady = true; }; // 连接关闭/报错时清理缓存,可在此补充自动重连逻辑 connection.onclose = connection.onerror = () => { this.connections.delete(name); }; // 按需添加消息处理逻辑 connection.onmessage = (e) => { console.log('收到服务端消息:', e.data); }; } send_stuff(name:string){ const conn = this.connections.get(name); // 发送前先校验连接状态 if(!conn || !conn.isReady || conn.ws.readyState !== WebSocket.OPEN){ // 可在此处添加消息缓存、自动重连逻辑 throw new Error('WebSocket连接不可用'); } conn.ws.send("stuff"); } }
关于你的猜想验证
你的猜想不成立,浏览器不会因为WebSocket对象没有被事件循环引用就主动断开连接。你后面给出的可运行示例没有问题,核心原因是它在onopen回调触发后才调用send(),此时连接已经完成握手处于可用状态,和引用是否在事件循环中无关。
内容的提问来源于stack exchange,提问作者RonsJurisdiction
相关产品推荐
相关产品推荐

