1005是协议层“未收到关闭帧”的表现,非传输错误,故浏览器不触发onerror事件;真正可靠的是onclose事件,它必触发且含event.code(1005时reason通常为空),所有状态归因逻辑须基于此。

WebSocket 1005 关闭时为什么拿不到 error 事件
因为 1005 是协议层“未收到关闭帧”的表现,不是传输错误,所以浏览器不会触发 onerror。你看到的 socket.onerror 可能根本不会执行,或者只返回空事件对象({ isTrusted: true }),没有任何可读线索。
真正可靠的是 onclose 事件——它一定会触发,且带 event.code 和 event.reason。但注意:event.code === 1005 时,event.reason 几乎总是空字符串,不能依赖它定位原因。
- 别在
onerror里做关键日志或重连判断 - 所有连接状态归因逻辑必须基于
onclose的code - 如果
onclose没触发,说明连接已“静默断开”,大概率是中间代理(如 Nginx、Spring Cloud Gateway)提前终止了 TCP 连接
服务端如何区分 1005 是客户端主动关还是网关截断
关键看服务端是否收到了合规的关闭帧。以 FastAPI 为例,WebSocketDisconnect 异常只在收到关闭帧后抛出;而 1005 对应的场景通常是:连接被强制中断,服务端甚至没机会进入 except 分支。
验证方法:在服务端日志中搜索 WebSocketDisconnect 是否出现。如果完全没记录,但客户端报 1005,基本可判定是反向代理或 TLS 层问题。
- Nginx 配置必须包含
proxy_http_version 1.1和proxy_set_header Upgrade $http_upgrade - Spring Cloud Gateway 需启用
spring.cloud.gateway.websocket.protected-headers=并检查max-frame-payload-length是否过小 - 用
tcpdump或 Wireshark 抓包,确认 FIN 包是否由服务端发出(正常)还是由网关 IP 发出(异常)
前端日志埋点该记录哪些字段才真正有用
只记 event.code 和时间戳远远不够。1005 的本质是“信息缺失”,所以你要补全上下文,让后续排查能交叉验证。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
每次 onclose 触发时,至少同步记录:
-
performance.now()时间戳(比Date.now()更精确) -
socket.url(确认是否走对了 wss/wss 路径) -
navigator.onLine状态(快速识别是否本地断网) -
document.visibilityState(页面切后台常导致浏览器冻结 WebSocket) - 上一次
onmessage的时间差(判断是否长时间无心跳)
示例代码片段:
socket.onclose = function(event) {
console.warn(`WS closed: code=${event.code}, url=${socket.url}`);
const log = {
code: event.code,
url: socket.url,
online: navigator.onLine,
visible: document.visibilityState,
idle_ms: performance.now() - lastMessageTime,
ts: performance.now()
};
// 发送到日志服务(注意避免日志本身触发新 WS 请求)
sendToLogger(log);
};
为什么重连逻辑里不能只看 1005 就立刻重试
因为 1005 本身不区分“可恢复”和“需暂停”的场景。比如用户关掉标签页、切到其他 App、或设备锁屏,此时重连不仅无效,还会浪费资源并干扰服务端连接池。
真正健壮的重连策略必须结合行为信号:
- 若
document.visibilityState === 'hidden'或pageHide事件已触发,延迟重连至少 30 秒 - 若连续两次
onclose的code都是1005,且间隔 - 若
navigator.onLine === false,停止重连,等待online事件
忽略这些条件直接轮询,容易把 1005 误判为网络抖动,反而掩盖真实问题——比如网关配置错误其实在首次连接时就失败了,但被重连掩盖,直到连接数打满服务端。










