应通过主动 websocket ping/pong 探测断连,配合退避重连、状态快照同步及前台可见性干预来保障 html5 游戏网络可靠性。

怎么判断 HTML5 游戏是否断网了
浏览器原生不提供“游戏连接断开”的事件,navigator.onLine 只反映页面级网络状态(比如关掉 Wi-Fi),但对 WebSocket 断连、服务器宕机、NAT 超时等完全无感。真正有效的检测必须靠主动探测。
推荐做法是:在 WebSocket 连接建立后,启动一个定时 ping 机制,服务端收到后立刻回 pong。客户端如果连续 2–3 次没收到响应,就判定为断线。
-
ping不要用XMLHttpRequest或fetch——它们走 HTTP,和游戏实时通道无关,容易误判 - 间隔别设太短(如 500ms),否则增加服务端压力;也别太长(如 30s),玩家卡住太久才反应过来
- 注意 WebSocket 的
readyState:断连瞬间可能是CLOSED或CLOSING,但有时会卡在OPEN却收不到数据——所以不能只依赖onclose
WebSocket 断连后如何安全重连
盲目轮询重连会触发浏览器限制(如 Chrome 对同一地址的快速连接失败有退避策略),还可能让服务端堆积大量半开连接。
关键点是:重连前清空旧引用、加退避、设上限。
立即学习“前端免费学习笔记(深入)”;
- 调用
socket.close()后立即把socket设为null,避免重复 close 或意外 send - 重连延迟按次数递增:第 1 次等 1s,第 2 次等 2s,第 3 次等 4s……超过 5 次就停,提示用户手动重试
- 重连过程中禁用游戏输入,防止操作堆积到重连后的消息队列里,造成逻辑错乱
- 如果使用
Socket.IO,别直接用它的reconnection选项——默认行为不校验服务端是否真恢复,建议关掉,自己控制
重连成功后怎么同步游戏状态
不是简单发个 “I’m back” 就完事。玩家可能在断线期间错过关键帧、道具拾取、血量变化,直接续上会导致状态撕裂。
服务端必须支持状态快照拉取,客户端拿到后做局部合并,而非全量覆盖。
- 重连握手阶段,客户端发
{"type":"sync_request","seq":123},其中seq是本地最后收到的消息序号 - 服务端返回
{"type":"sync_data","state":{...},"from_seq":123,"to_seq":189},包含从 123 到 189 的所有增量事件 - 客户端逐条重放这些事件,跳过已处理过的
seq,避免重复扣血或重复掉落 - 严禁用
location.reload()或重建 Canvas 上下文——会丢失未提交的操作(比如拖拽中松手)
移动端断连特别要注意什么
iOS Safari 和 Android WebView 对后台标签页的 WebSocket 处理极不一致:有的直接静默关闭,有的保留连接但不触发 onmessage,有的甚至伪造 onclose。
必须监听页面可见性变化,并主动干预。
- 监听
document.visibilityState,切到后台时主动socket.close(),并标记“疑似挂起” - 切回前台时,不立即重连,先检查
socket.readyState === WebSocket.OPEN;如果不是,再走完整重连流程 - Android WebView 中,
WebSocket在锁屏后大概率失效,但visibilitychange不一定触发,建议加一个 30s 的心跳保活 fallback - 别依赖
beforeunload——移动端基本不触发,且游戏里不该阻止页面卸载
断线逻辑看着简单,实际最麻烦的是状态一致性:服务端快照粒度、客户端事件重放顺序、UI 渲染时机三者稍有错位,就会出现“我明明捡了药瓶却没加血”这类问题。多打日志,少信假设。











