必须同时实现心跳检测与有策略的自动重连:心跳用业务消息模拟(如{"type":"ping"}),服务端立即回{"type":"pong"},前端超时未收则主动close;重连需指数退避(1s→2s→4s…上限30秒)、次数限制(5–10次)、code判断(1006或≥4000才重连)及凭证刷新。

WebSocket连接在真实网络中极少能“一直在线”,不加心跳和重连,30秒内就可能被NAT、防火墙或代理静默断开,且前端毫无感知。必须同时实现心跳检测 + 有策略的自动重连,二者缺一不可。
为什么onclose触发太晚?必须加心跳
仅靠监听onclose无法应对“假连接”:TCP层已断但WebSocket状态仍为OPEN,send()不报错、消息发不出、服务端也收不到——用户卡在“发送中”却无响应。这是最典型的静默失败。
- 浏览器 WebSocket API 不暴露原生
ping帧发送能力,所以实际心跳得用业务消息模拟,如{"type":"ping"} - 服务端收到后必须立即回
{"type":"pong"},不能排队或延迟响应 - 前端需在发
ping时启动一个独立定时器(比如5秒),超时未收pong就主动调用ws.close(4999, "no_pong"),强制触发onclose流程 - 心跳间隔建议30秒,超时阈值设为5–10秒(不是心跳间隔的倍数)——太长会延迟发现断连,太短易误判
ws.readyState === WebSocket.CLOSED时还调close()会报错
错误信息"WebSocket is already CLOSED"通常出现在重连逻辑里:上一次连接已进入CLOSED状态,代码却仍尝试ws.close()或ws.send()。这不是浏览器bug,而是状态机没管住。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 每次新建连接前,先检查
wsRef是否存在且readyState为OPEN或CONNECTING;若为CLOSING或CLOSED,直接跳过close()调用 - 在
onopen中才给wsRef赋值,在onclose或onerror中立即将其置为null - 避免在
onclose回调里直接调connect(),要加isReconnecting标志位防重复触发 - 手动调用
ws.close(code, reason)后,状态不会立刻变CLOSED,中间存在CLOSING过渡期,此时send()仍会抛错
重连不能“立刻再试”,得用指数退避+上限控制
连续快速重连不仅浪费资源,还可能触发服务端限流(如Nginx的limit_conn)或让客户端陷入死循环。关键不是“连上为止”,而是“连得聪明”。
- 起始延迟从1秒开始,每次翻倍:
1s → 2s → 4s → 8s → 16s,上限建议30秒 - 总重试次数硬限制为5–10次,超限后停止自动重连,改由用户手动刷新或弹提示
-
onclose事件里的event.code必须判断:1000(正常关闭)不重连,1006(网络异常)或≥4000(业务错误)才走重连逻辑 - 重连前检查认证凭证(如JWT
token)是否过期,过期则先调登录接口刷新,再重连——否则会陷入“重连→401→重连”的死循环
用ReconnectingWebSocket库省事,但别当黑盒
第三方库如ReconnectingWebSocket封装了重连逻辑,能快速落地,但它不自动处理心跳、凭证刷新和业务协议解析——这些仍得你补。
- 引入后仍需自己启动心跳定时器,并在
onmessage里识别pong响应 - 库的
reconnectInterval只控制重连发起节奏,不替代心跳保活;两者是正交机制 - 务必设置
maxReconnectAttempts和timeoutInterval,否则默认行为可能不符合生产要求 - 调试时打开
debug = true,它会在控制台输出每次重连尝试、状态变更和错误堆栈,比手写日志更直观
真正难的不是写对某一行代码,而是在onopen、onmessage、onclose、onerror四个回调之间维持一致的状态视图,并让心跳、重连、凭证、业务消息全部跑在同一个状态机下。漏掉任意一环,都可能让连接“看起来连着,其实废了”。










