websocket“掉线”实为网络中间设备清理闲置连接,保活需覆盖传输层、代理层、应用层和移动端四层限制:nginx与运营商nat需合理配置超时及keepalive;移动端须用pagevisibility api规避后台冻结;服务端须主动维护心跳时间戳并淘汰僵死连接;重连应区分关闭码、指数退避且避免并发心跳。

WebSocket 连接“掉线”不是代码写错了,而是网络中间设备在帮你清理闲置连接——Nginx、运营商 NAT、手机系统都在等你沉默满 30–60 秒后直接掐断。保活的关键不是“连得上”,是“一直动”。真正有效的策略必须覆盖传输层、代理层、应用层和移动端限制四层。
一、穿透 Nginx 和运营商 NAT 的超时限制
Nginx 默认 proxy_read_timeout=60s,但运营商 NAT 空闲超时通常只有 30–120 秒,且不可配置。单纯把 timeout 设成 86400 秒没用,因为它是“从后端读数据”的等待上限,不阻止上游先动手。
- 必须设 proxy_read_timeout 和 proxy_send_timeout 均 ≥ 心跳间隔的 2–3 倍(如心跳 30s,建议设为 90–120s)
- 启用 proxy_socket_keepalive on,并调小内核参数:
tcp_keepalive_time 300(5 分钟)、tcp_keepalive_interval 15 - 务必配齐 Upgrade 协议升级头:
proxy_set_header Upgrade $http_upgrade和Connection "upgrade"
二、绕过移动端后台冻结机制
iOS Safari 后台标签页会在 10–20 秒内冻结 setInterval;Android Chrome 在低内存或省电模式下也会暂停 WebSocket 发送。原生定时器心跳在手机上基本失效。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 用 PageVisibility API 监听
visibilitychange:前台时恢复心跳,后台时暂停发送但保留连接状态 - 后台期间可改用 Notification API 或 Background Sync 做轻量唤醒(需 HTTPS + Service Worker)
- 心跳消息统一用 JSON 格式,如
{"type": "heartbeat"},避免纯字符串被代理截断或忽略
三、服务端主动管理连接活性
不能只靠客户端发 PING,服务端必须自己维护每个连接的最后活跃时间戳,并主动淘汰僵死连接。
- 收到心跳后更新连接的 last_heartbeat_at 时间戳
- 用 setTimeout 设置连接级超时检查(如 90s 未更新则 close),防止堆积
- 别依赖浏览器原生 ping/pong 帧——它无法穿透多数代理;所有心跳走普通
message通道 - Spring Boot 中用
@MessageMapping("/ws/heartbeat")接收并立即回{"type":"pong"}
四、重连逻辑必须带退避与原因识别
“断了就立刻重试”在弱网下会触发雪崩式请求,压垮网关。健壮重连要区分关闭类型、控制节奏、避免盲目轮询。
- 只对 event.code === 1006(异常关闭) 或 1012(服务重启) 启动重连
- 1001(端点离开) 属于用户主动行为,不应自动重连
- 首次延迟 ≥1s,后续按指数退避:1s → 2s → 4s → 8s,上限不超过 30 秒
- 重连前清空旧定时器,重连成功后重置心跳计时器,避免多套心跳并发










