yl-backend/docs/ws.md

2.7 KiB
Raw Blame History

你提的这个问题很有代表性——如何区分 WebSocket 是“用户主动断开”还是“异常断线”,确实是一个经典难题。

目前主流的解决方案其实都是「绕过法」,原因是: 🔒 浏览器并不会告诉你页面是“关闭了”还是“断网了”——WebSocket.onclose 都会触发。

所以目前更好的思路是: 【方案一】手动标记断开意图(已介绍,业界最常用) js 复制 编辑 let manuallyClosed = false

function closeWebSocket() { manuallyClosed = true ws.close() }

ws.onclose = () => { if (!manuallyClosed) { // 是异常断线 reconnect() } } 这种方式简单、可靠,你控制了连接断开的“意图”。

【方案二】结合 window.onbeforeunload 判断页面关闭 你可以在用户关闭页面或刷新时做标记:

js 复制 编辑 window.addEventListener("beforeunload", () => { manuallyClosed = true chatWs?.close() }) 这和第一种方案可以配合使用,适用于 Vue、React、原生项目等。

【方案三】心跳机制 + 超时判断(进阶) 逻辑: 前端每隔 N 秒发送一个 ping

后端收到后回一个 pong

如果前端 N 秒没收到 pong说明服务挂了 → 手动调用重连;

如果连接是被主动 close() 的,不发送 ping。

优点: 能更快判断“假连接”(服务端挂了但浏览器还没断);

能做“连接质量监测”。

这种方式通常配合方案一一起使用。

【方案四】统一封装 WebSocket 管理类(推荐) 比如你封装一个 WebSocketManager内部处理

是否需要重连;

是否是用户主动关闭;

心跳检测;

最大重连次数限制;

事件监听。

封装后你只用关注:

js 复制 编辑 WebSocketManager.connect() WebSocketManager.send("xxx") WebSocketManager.close() 优势:避免在多个组件重复写 onclose/onerror 等逻辑。

总结对比 方案 能力 可行性 说明 手动标记断开意图 区分主动 vs 异常 👍 极推荐 最直接可靠 onbeforeunload 页面标记 判断刷新/关闭 👍 建议与上方案配合 心跳机制 检测假连接 👍 中大型项目推荐 适合服务端也配合开发 封装统一管理器 解耦复杂逻辑 👍 建议中后期项目使用 支持多频道、多状态

🧩 总结一句话: WebSocket 是单向的,不告诉你“为什么断了”,你只能自己判断。手动标记 + 心跳检测 + 页面事件,是目前最佳组合策略。

如你需要我给出一个封装好的 WebSocketManager 管理类含标记断开、心跳、自动重连、事件订阅等可以告诉我使用的框架Vue/React/原生),我可以直接给你封装好一份。