前端WebSocket断线重连必须由JS自主实现,因服务端无客户端逻辑;需用业务心跳(如{"type":"ping"})主动探测“假连接”,间隔20–30秒且小于服务端空闲超时;配合指数退避+抖动重连(上限30秒、≤10次)、资源清理、token刷新及统一超时判定机制。

前端 WebSocket 断线重连不能交给 Swoole 处理——think-swoole、Swoole\WebSocket\Server 等服务端组件不提供客户端重连逻辑,这事必须由 JS 自己做,且得做对。
onclose 为什么经常不触发?
很多“闪断”根本不会走到 onclose:TCP 已断,但 ws.readyState 还是 1(OPEN),消息发不出也收不到。浏览器不会主动上报这种“假连接”。
- 典型现象:
ws.send()静默失败,onmessage停止接收,但onclose和onerror都没调用 - 根本原因:WebSocket 协议本身无内置健康检查,依赖应用层心跳探测
- 必须自己发业务心跳(比如
{"type":"ping"}),不能依赖浏览器原生 ping/pong(JS API 不暴露发送接口) - 建议心跳间隔设为 20–30 秒,且必须 小于服务端空闲超时(如 Nginx 的
proxy_read_timeout默认 60s,Swoole 的heartbeat_idle_time若设为 30s,则心跳应 ≤25s)
重连不能“一断就试”,要防雪崩
固定 1s 或 2s 重试,在弱网恢复或服务端重启瞬间,所有客户端会齐刷刷涌上来,打爆连接队列。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 用指数退避 + 随机抖动:
delay = Math.min(1000 * Math.pow(2, retryCount), 30000),再叠加 ±30% 随机值 - 总重试次数硬限制为 ≤10 次,超限后停自动重连,改提示用户手动刷新
-
retryCount必须在onopen中清零,否则旧计数残留会导致下一次重连直接跳过 - 避免
onclose和onerror同时触发导致双重重连:加isReconnecting标志位,重连中直接 return
每次重连前必须清理干净
反复重连若不清除上一轮资源,会出现多个 WebSocket 实例并发、定时器堆叠、事件监听器重复绑定、内存泄漏甚至 token 冲突。
- 用唯一引用管理连接,例如
let wsRef = null;新建前先执行wsRef?.close()并置空 - 清除上一轮心跳:
clearInterval(pingInterval)、clearTimeout(heartbeatTimeout) - 移除所有事件监听器(
ws.removeEventListener或改用一次性绑定 + 显式解绑) - 若 URL 含 token,重连前必须重新获取——旧 token 很可能已过期,复用只会陷入“重连→401→重连”死循环
最易被忽略的一点:心跳响应超时判定和重连触发是两件事,但必须共用同一套计时逻辑。比如发完 {"type":"ping"} 后启动 5 秒 setTimeout,若未收到 {"type":"pong"} 就主动 ws.close(4999, "no_pong"),而不是等 onclose 被动回调——这才是真正可控的断线感知。

















