WebSocket onerror仅捕获底层网络错误,握手失败需通过onclose的code与reason判断;应采用指数退避重连、发送前校验readyState、应用层实现ping/pong心跳保活。

WebSocket 连接建立失败时,onerror 不触发怎么办
浏览器中 WebSocket 实例的 onerror 事件**只在连接过程中发生底层网络错误(如 DNS 失败、TLS 握手异常)时触发**,而绝大多数常见失败——比如服务端拒绝(HTTP 400/401)、路径不存在(404)、协议升级失败(502/503)——都发生在 HTTP 握手阶段,此时 WebSocket 对象已创建但尚未进入 OPEN 状态,onerror 不会调用,onclose 会触发且 event.code 通常为 1006(表示“abnormal closure”),但无具体原因。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 不要依赖
onerror捕获握手失败,改用onclose+event.code和event.reason组合判断; - 在
new WebSocket(url)后立即设置onopen、onclose、onmessage,避免竞态; - 对关键业务连接,可在
onclose中检查event.code !== 1000 && event.code !== 1001,并记录event.reason(部分服务端会在Sec-WebSocket-Close-Reason中透传); - 若需更早感知失败(如鉴权失败),后端应在 Upgrade 响应头中返回明确状态码,并前端配合预检请求(
fetch或HEAD)做轻量校验——但这会增加 RTT,仅适用于低频连接或强校验场景。
频繁重连导致连接雪崩,如何节流与退避
未加控制的自动重连(尤其在服务端宕机或网络抖动时)会瞬间产生大量 TCP 握手和 TLS 协商,既压垮客户端资源,也加重服务端负载。浏览器本身不限制 WebSocket 实例数量,但 OS 层有 socket 句柄上限(Linux 默认 1024),超出后新连接直接失败。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 使用指数退避(exponential backoff),初始延迟 1s,每次失败 ×1.5~2,上限设为 30s,避免固定间隔造成同步重连洪峰;
- 限制最大重试次数(如 5 次),之后暂停自动重连,交由用户手动触发或降级为轮询;
- 在重连前检查
navigator.onLine,但注意它仅反映系统网络开关状态,不保证可达性,建议结合心跳超时判定; - 用
setTimeout而非setInterval控制重连调度,避免多个定时器叠加; - 每次新建
WebSocket前,确保清除旧实例的事件监听器(ws.onmessage = null等),防止内存泄漏。
send() 报错 InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state
这是最常被忽略的时序问题:WebSocket 创建后是 CONNECTING(0)状态,只有收到 onopen 回调才变为 OPEN(1)。在 onopen 外直接调用 send(),必然报这个错。但更隐蔽的是,有些封装库把 send 加入队列却未正确处理连接未就绪状态,导致消息丢失。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 所有发送逻辑必须包裹在
if (ws.readyState === WebSocket.OPEN)判断中; - 若需支持连接中缓存发送,自行维护一个待发队列(
Array),在onopen中遍历发送,并清空队列; - 避免在
onopen中立即发多条消息——某些老旧代理或中间件对连续帧处理不稳定,可加setTimeout(() => {}, 0)微任务分隔; - 发送前检查
ws.bufferedAmount,若远大于 0(如 > 64KB),说明底层缓冲区积压严重,应暂缓发送或丢弃低优先级消息。
心跳保活失效,连接被中间设备静默断开
运营商 NAT、企业防火墙、CDN、反向代理(如 Nginx)普遍设置 60~300 秒空闲超时,若 WebSocket 连接期间无任何数据帧,会被单向或双向切断,且不发 FIN 包,客户端长时间收不到 onclose,表现为“假在线”。浏览器自身不提供心跳机制,必须由应用层实现。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 心跳必须用
ping/pong帧(二进制或文本),不能只靠send('')——后者生成的是 data frame,部分中间件不将其视作活跃信号; - 服务端必须响应
ping并发pong,前端每 30 秒发一次ping,超时 5 秒未收到pong则主动close(); - 心跳消息内容建议带时间戳(如
JSON.stringify({t: Date.now(), type: 'ping'})),便于排查时序问题; - Nginx 配置需显式开启 WebSocket 支持:
proxy_http_version 1.1;、proxy_set_header Upgrade $http_upgrade;、proxy_set_header Connection "upgrade";,缺一不可; - 避免在心跳回调中执行重连逻辑——心跳失败应先 close 再重建,而不是复用原实例。
真正难的不是写通 WebSocket,而是让连接在各种中间设备、弱网、进程休眠、页面后台运行等现实条件下稳定存活。很多问题不会立刻暴露,要等上线后流量上来、不同网络环境混合出现时才集中爆发。


















