yl-backend/docs/ws.md

88 lines
2.7 KiB
Markdown
Raw Normal View History

2025-05-14 00:14:06 +08:00
你提的这个问题很有代表性——如何区分 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/原生),我可以直接给你封装好一份。