WebSocket异常断开无法通过onerror捕获,必须监听onclose并检查event.code(如1006),它由中间件强制RST导致,需对齐心跳与各层超时配置、实现带指数退避和状态锁的重连机制。

WebSocket异常断开不能靠onerror捕获,必须监听onclose并检查event.code——尤其是1006这种“静默断连”,它不触发onerror,也不带reason,但几乎占生产环境断连的70%以上。
如何正确识别1006异常断开
看到onclose被触发且event.code === 1006,别急着改业务逻辑。这代表TCP连接已被某层(Nginx、LB、Node.js server.timeout、iOS后台回收)强制RST,WebSocket协议层甚至没机会发close frame。
-
onerror对1006完全静默,加再多try/catch也没用 - Chrome DevTools → Network → WS → Frames里,最后一条是
Ping但无Pong回应,基本可锁定服务端或中间件未透传心跳 - 移动端切后台后iOS Safari通常30秒内断连,Android WebView行为不一,需监听
visibilitychange主动close()再重连 - 若断连时间高度规律(如固定60秒、300秒),优先查Nginx
proxy_read_timeout、TomcatconnectionTimeout、AWS ALB空闲超时等中间层配置
客户端重连必须带指数退避和状态锁
裸调new WebSocket()重连等于给服务端刷DDoS。真实场景下要控制并发、避免雪崩,并防止重连过程中重复初始化。
- 用
reconnectAttempts计数 +setTimeout(..., 1000 * Math.pow(2, attempts))实现指数退避,上限建议maxReconnectAttempts = 5 - 在
onopen回调里重置计数器,否则一次成功后下次断连会沿用旧计数 - 用
let socket = null+if (socket && socket.readyState === WebSocket.CONNECTING) return避免重复发起连接 - 重连前先
socket?.close(1000, 'reconnecting'),避免残留连接占用资源
Nginx/Tomcat/Node.js关键超时参数必须对齐心跳
服务端和中间件的超时值如果小于心跳周期,等于主动制造1006。它们不是建议值,而是硬性约束。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- Nginx必须配全三项:
proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade";proxy_read_timeout至少设为90(心跳30秒 × 2 + 抖动余量) - Tomcat的
connectionTimeout(单位ms)建议≥3600000(1小时),写在server.xml的Connector标签内 - Node.js原生
http.Server默认timeout = 60000,使用ws.Server({ noServer: true })时,必须显式调用server.on('upgrade', ...)接管socket,否则60秒后内核直接RST - AWS ALB空闲超时固定60秒不可调,高可用场景应换NLB,或把心跳压缩到≤20秒
心跳检测不能只靠客户端单向发送
只发ping不验pong,等于没心跳。真正有效的保活需要双向确认+超时判定。
- 客户端每30秒
send(JSON.stringify({type: 'ping'})),同时启动setTimeout等待服务端pong,超时(建议5秒)即视为断连 - 服务端收到
ping必须立即回pong,不能进业务队列排队;Spring Boot中可通过session.setMaxIdleTimeout(30000)辅助,但不能替代应用层心跳 - 避免用
WebSocket.ping()这类底层API——浏览器暂未标准化,ping/pong帧只能通过文本消息模拟 - 心跳消息体尽量轻量,不要带时间戳、签名等冗余字段,减少序列化开销和丢包概率
最常被忽略的一点:所有中间层(CDN、WAF、SLB)都可能按空闲时间切断连接,而它们的超时策略往往比你的代码更“霸道”。上线前务必用curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" wss://your-domain/ws绕过浏览器,直连验证链路是否真通。










