WebSocket 的 onerror 事件不能可靠捕获握手失败(如 401、403、404),仅响应网络层异常;握手失败时浏览器直接关闭连接,不触发 onerror,而是跳过 onopen、触发 onclose(code 通常为 1006 或 0)。

WebSocket 的 onerror 事件**不能可靠捕获握手失败**(如 401、403、404 等 HTTP 状态码错误),它主要响应底层网络层异常(如 DNS 失败、连接被重置、TLS 握手失败等),且触发时机不统一、信息有限。真正想处理握手失败,必须依赖 onopen / onclose 配合超时机制和状态判断。
为什么 onerror 捕不到 4xx/5xx 握手错误
WebSocket 握手本质是 HTTP 请求(Upgrade),但浏览器在收到非 101 响应时,会直接关闭连接,**不触发 onerror,而是跳过 onopen,最终触发 onclose(且 event.code 通常为 1006 或 0,event.reason 为空)**。这是规范行为,并非 bug。
如何实际捕获握手失败
用「连接超时 + close 事件分析」组合策略:
- 创建 WebSocket 实例后立即启动一个定时器(例如 5 秒)
- 如果
onopen触发,清除定时器,视为成功 - 如果定时器先触发,说明既没 open 也没 error,极大概率是握手卡住或被拒绝
- 监听
onclose:若event.code === 1006(abnormal closure)且无event.reason,基本可判定握手失败 - 注意:某些环境(如 Safari)可能对 401/403 直接触发
onerror,但不可依赖,需兼容处理
onerror 能捕获哪些真实错误
onerror 主要响应以下情况:
立即学习“Java免费学习笔记(深入)”;
- DNS 解析失败(如域名不存在)
- TCP 连接被防火墙/代理中断
- TLS 证书验证失败(自签名证书未信任)
- 客户端主动断网瞬间尝试连接
- 注意:
event对象通常只有type字段(值为 "error"),没有错误码或消息,无法区分具体原因
推荐的健壮连接写法
结合超时、close 分析和重试逻辑:
function connectWithRetry(url, maxRetries = 3) {
let ws;
let timeoutId;
let retryCount = 0;
<p>function connect() {
ws = new WebSocket(url);</p><pre class="brush:php;toolbar:false;">ws.onopen = () => {
clearTimeout(timeoutId);
console.log('WebSocket connected');
// 后续业务逻辑
};
ws.onerror = (e) => {
console.warn('WebSocket network-level error:', e);
// 可记录日志,但不用于决策重试
};
ws.onclose = (e) => {
clearTimeout(timeoutId);
if (e.code === 1006 || e.code === 0) {
console.error('Handshake failed or connection dropped');
maybeRetry();
} else {
console.info('Closed normally:', e.code, e.reason);
}
};
timeoutId = setTimeout(() => {
if (ws.readyState === WebSocket.CONNECTING) {
console.warn('Connection timed out — likely handshake failure');
ws.close(); // 主动关闭触发 onclose
maybeRetry();
}
}, 5000);}
function maybeRetry() { if (retryCount
connect(); }
补充:服务端配合提升可观测性
前端无法获取 HTTP 响应头,但服务端可在握手失败时,在关闭前发送带提示的 close 帧(如 code=4001, reason="Unauthorized"),前端在 onclose 中读取 event.code 和 event.reason,比纯猜更准。不过这需要服务端支持 WebSocket 协议级 close 帧定制。


















