原生 WebSocket 不自带重连和统一错误处理,必须封装;onerror 是哑事件,不带状态码且触发不可靠,重连决策必须依赖 onclose 的 code 和 wasClean 字段——1000 不重连,1006 立即重连,4000+ 业务码需定向处理。
直接说结论:原生 websocket 不自带重连和统一错误处理,必须自己封装;不处理 onclose 的 code 和 onerror 的触发边界,重连逻辑大概率会无限循环或漏判。
为什么 onerror 不能单独作为重连依据
onerror 是一个“哑事件”:它不携带状态码、不区分网络抖动还是握手失败、甚至在某些浏览器中可能不触发(比如连接未建立就断网)。只监听它会导致两种典型问题:
- 连接还没建好就报错,
ws.readyState还是0(CONNECTING),此时调用ws.close()会抛异常 - 服务端主动关闭时(如
code=4001业务登出),onerror仍会被触发,但你不该重连 - 真正可靠的判断依据是
onclose中的event.code和event.wasClean
实操建议:把 onerror 当作日志兜底项,仅记录 + 上报,重连决策一律交给 onclose 处理。
onclose 状态码怎么分情况处理
不同 code 含义差异极大,硬编码成“都重连”是常见翻车点:
-
1000:正常关闭 → 不重连,可能是用户登出或页面卸载,应清空定时器、释放资源 -
1006:异常关闭(最常见)→ 立即启动重连,大概率是网络中断或服务崩溃 -
4000–4999:自定义业务码 → 比如4001表示 token 过期,应跳登录页,而非重连 -
1001或1005:端点离开 / 无原因关闭 → 视同1006,但可降级为延迟重连(比如首次 1s,后续指数退避)
关键代码片段:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
socket.onclose = (event) => {
if (event.code === 1000) {
console.info('主动关闭,不重连');
return;
}
if (event.code >= 4000 && event.code < 5000) {
handleBusinessClose(event.code);
return;
}
if (event.code === 1006 || !event.wasClean) {
startReconnect();
}
};
心跳包发什么、什么时候发、发了没响应怎么办
心跳不是“随便 ping 一下”,它要解决两个问题:防网关超时断连 + 探活。很多封装漏掉第二点:
- 发什么:推荐字符串
'ping'(轻量、无歧义),避免发空字符串或 JSON(服务端解析成本高) - 什么时候发:连接
open后立即启动定时器,间隔建议 25–30 秒(低于多数云网关默认 60s 超时) - 没响应:不能只等
pong,要设服务端响应超时(比如 5s),超时则主动close()并触发重连 —— 否则客户端以为还活着,实际已被服务端静默踢出
注意:onmessage 收到 'pong' 后,必须重置响应超时定时器,否则一次网络抖动就会误判为断连。
重连时消息队列怎么缓存才不丢数据
重连期间新消息不能直接丢,但也不能无脑堆进数组 —— 容易内存爆炸或顺序错乱:
- 只缓存
send()调用时连接未就绪(readyState !== 1)的消息,已发送成功的不用缓存 - 缓存结构用
Array<{ data: string, timestamp: number }>,带时间戳便于调试和超时清理 - 重连成功后,按顺序
send(),并加简单重试计数(比如最多发 3 次,失败则丢弃 + 上报) - 缓存上限设为 50 条,超出后
shift()丢弃最老消息,防止离线太久导致堆积
容易被忽略的一点:重连成功后,要先清空旧定时器(包括心跳和响应超时),再启动新的,否则多个定时器并发运行会导致心跳错乱或重复发送。

















