页面关闭时应主动触发 websocket 关闭,通过 beforeunload 或 pagehide 事件调用 socket.close(1000, "page unloaded") 发送标准关闭帧,并配合服务端完成 rfc 6455 四次挥手式关闭,同时清理本地状态、处理非预期中断并启用退避重连。

页面关闭时主动触发 WebSocket 关闭,是避免连接残留、资源泄漏和客户端状态错乱的关键一步。直接让浏览器静默断开 TCP 连接,会导致服务端无法识别意图、前端收不到 event.code 和 event.reason,重连逻辑也容易误判。
监听 beforeunload 事件并主动 close
在用户关闭标签页或刷新页面前,利用 beforeunload 捕获时机,调用 socket.close() 发送标准关闭帧:
- 必须传入合法状态码(如
1000表示正常关闭)和可读原因,例如socket.close(1000, "Page unloaded") - 避免在该回调中执行异步操作(如 await 或 fetch),因浏览器可能已暂停脚本执行
- 若 socket 已处于
CLOSED或CLOSING状态,调用 close 不会报错,可安全调用
确保关闭帧被真正发出
部分浏览器(尤其移动端)可能在 beforeunload 中限制网络行为,导致关闭帧未发出。可做双重保障:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 在
pagehide事件中再次尝试关闭(该事件比beforeunload更可靠,且支持 visibilityState 判断) - 服务端应设置合理超时(如 30 秒无心跳即清理),不依赖客户端一定发关闭帧
- 前端可记录最后发送时间,在
onclose中结合event.wasClean判断是否为预期关闭
配合服务端完成握手闭环
仅前端调用 close() 不足以保证协议合规——服务端收到关闭帧后,需回传相同关闭帧并调用底层 CloseAsync(.NET)或 WriteMessage(websocket.CloseMessage, ...)(Go/Gorilla)等,才算完成 RFC 6455 规定的四次挥手式关闭:
- 服务端若忽略响应,前端
onclose中event.wasClean将为false,应触发降级重连策略 - 状态码需匹配:前端发
1000,服务端响应也应为1000;若服务端返回1001,说明它判定为“端点离开”,逻辑上一致 - 原因字符串长度建议控制在 123 字节内(RFC 限制),避免截断或解析失败
关闭后的状态清理与降级兜底
即使主动关闭成功,也要做好本地状态归零和异常 fallback:
- 清除定时器(如心跳
setInterval)、取消 pending 的send()调用,避免InvalidStateError - 将 socket 实例置为
null,防止后续误用 - 若
onclose中event.code === 1006或wasClean === false,说明非预期中断,可启动带退避的自动重连(如 1s → 2s → 4s)










