弱网下WebSocket需主动心跳探测与节制重连:25–30秒发心跳并5–8秒超时关闭;重连用指数退避(1s/2s/4s…,上限5次)、状态锁防并发;握手设8–15秒超时降级;依onclose.code精准决策重连或鉴权。

弱网环境下 WebSocket 连接重试不能靠被动等待,必须主动控制节奏、识别真实断连、避免风暴式重连。关键不在“多试几次”,而在“什么时候试、试多少次、怎么试才不伤服务端和用户体验”。
用心跳探测代替等待 onclose
弱网下连接常处于“假死”状态:socket.readyState 仍是 OPEN,但底层 TCP 已中断,onclose 和 onerror 都不会触发。此时必须靠应用层心跳提前发现异常:
- 每 25–30 秒发送一次
{"type":"heartbeat"},服务端立即返回{"type":"ack"} - 每次发心跳后启动独立定时器(建议 5–8 秒超时),未收到 ack 就主动调用
socket.close(4999, 'heartbeat timeout') - 不要依赖浏览器原生 ping/pong——部分旧版 Safari 不暴露
onpong,业务层心跳更可控、更兼容
重连必须带节制策略
裸调 new WebSocket() 重连等同于对服务端发起简易 DDoS。真实场景中要严格约束重连行为:
- 使用指数退避:第 1 次延迟 1s,第 2 次 2s,第 3 次 4s……上限建议设为 5 次,最大间隔不超过 30 秒
- 每次重连前先执行
socket?.close(1000, 'reconnecting'),防止残留连接堆积 - 用状态锁(如
let isReconnecting = false)+if (socket?.readyState === WebSocket.CONNECTING) return阻止并发重连 - onopen 回调里重置重连计数器,否则上次失败的指数会延续到下一次断连
握手阶段就要防超时
极端弱网下,WebSocket 握手(HTTP Upgrade 请求)可能卡在 DNS、TLS 或代理链路上,浏览器不提供超时控制权,必须自己兜底:
立即学习“Java免费学习笔记(深入)”;
- 创建 socket 后立即启动 8–15 秒定时器,绑定唯一 ID;onopen 或 onerror 触发时清除它
- 超时未就绪,就主动放弃并降级(如切到 SSE 或轮询),避免用户长时间卡在 loading
- 连接前可用
navigator.onLine快速过滤明显离线状态,再加一个轻量 HTTP HEAD 探测(如/health?ts=xxx)验证链路可达性 - URL 中可携带网络类型参数(如
?net=2g),服务端据此关闭非必要中间件,缩短握手路径
精准识别断开原因,区别对待
不是所有断开都要重连。onclose 是唯一可靠入口,但必须看 event.code 才能决策:
-
code === 1006:TCP 被强制 RST(Nginx、运营商 NAT、iOS 后台回收),需立即重连 -
code === 1001或reason含 “going away”:服务端主动关,查日志是否因 heartbeat timeout,通常也要重连 -
code === 4001或reason含 “auth failed”:属 token 过期等业务问题,应先刷新凭证,而非盲目重连 -
wasClean === false且code为空:大概率是静默断连,按 1006 处理


















