WebSocket close code 1006 为何没有服务端原因
来源:17golang原创
时间:2026-09-11 14:57:36 154浏览 收藏
浏览器里看到 WebSocket close code 1006,却发现 event.reason 是空字符串,服务端也没有“主动关闭”的记录,这并不表示服务端漏写了原因。1006 是 WebSocket 规范保留的异常关闭表示:连接结束时没有收到 Close 帧,浏览器只能告诉你“连接非正常结束”,不能凭这个数字还原真正的故障原因。
排查时要把客户端事件、反向代理连接日志和应用日志放在同一条时间线上。只有应用确实发出了 Close 帧,客户端才能拿到对应的状态码和 reason;如果是代理超时、网络切换、进程崩溃或 TCP/TLS 连接被直接切断,浏览器通常只会落到 1006。
- 1006 是异常关闭的本地表示,不能由服务端放进 Close 帧发送。
- 先记录
code、reason、wasClean和时间,再去对齐三层日志。 - 修复方向取决于断点:应用关闭、网关超时和传输中断的处理方式不同。
看到 1006 时,先把它理解为“没有完成 WebSocket 关闭握手”,而不是“服务端返回了一个叫 1006 的业务错误”。
WebSocket 1006 为什么没有服务端原因
RFC 6455 将 1006 定义为 Abnormal Closure,并明确它是保留值,不能作为 Close 控制帧里的状态码。浏览器的 CloseEvent.code 在没有收到 Close 帧时使用这个值,CloseEvent.reason 因而没有服务端提供的文本,wasClean 通常也会是 false。
这解释了一个常见误会:服务端日志里写了“准备关闭”,不等于客户端收到了 Close 帧。进程可能在写帧前崩溃,代理可能在转发前超时,或者移动网络切换让底层连接先断掉。此时浏览器只能报告结果,不能替你判断断点。

先把 CloseEvent 的四个字段记完整
不要只打印 event.code。将四个字段和连接建立时间、最后一条消息时间一起记录,才能判断是刚握手就断,还是空闲一段时间后被切断。
socket.addEventListener('close', (event) => {
// 记录客户端能观察到的全部关闭信息,方便和服务端日志按时间对齐。
console.warn('WebSocket closed', {
code: event.code,
reason: event.reason || '(empty)',
wasClean: event.wasClean,
closedAt: new Date().toISOString(),
});
});
判断可以先按这个表分层:
| 现象 | 更可能说明什么 | 先查哪里 |
|---|---|---|
1000 且 wasClean=true | 完成了关闭握手 | 业务关闭原因和用户操作 |
1006、reason 为空 | 未收到 Close 帧 | 代理超时、进程退出、网络链路 |
1008 或 1009 | 对端发送了明确的协议/策略结果 | 服务端策略和消息大小限制 |
按客户端、代理、服务端三层查断点
第一层看浏览器:确认连接 URL、握手是否成功、最后一次收发消息的时间,以及页面是否发生切换或网络类型变化。第二层看网关:检查 WebSocket Upgrade 是否被保持、空闲超时是否短于业务心跳间隔、是否有 upstream reset 或连接被回收。第三层看应用:确认是否进入了主动关闭分支、是否在写 Close 帧前异常退出、是否因部署或健康检查被终止。
最有价值的不是一句“连接断了”,而是同一个连接标识在三层日志中的最后状态。例如应用有“发送 1000”的记录而网关没有对应出站帧,优先查代理;应用完全没有关闭记录但进程刚重启,优先查进程生命周期;三层都没有错误而客户端恰好切换网络,则要保留传输中断的可能。

修复后用重连和心跳验证结果
重连可以恢复体验,但不能掩盖原因。客户端应使用递增退避和上限,避免服务端或代理异常时瞬间建立大量新连接;重连前保存必要的订阅状态,重连成功后再补订阅。心跳间隔要小于代理的空闲超时,并在服务端确认收到心跳后更新连接活跃时间。
function reconnectWithBackoff(connect, attempt) {
// 退避上限避免 1006 连续出现时形成重连风暴。
const delay = Math.min(30000, 1000 * 2 ** Math.min(attempt, 5));
return window.setTimeout(connect, delay);
}
如果用户主动退出页面,先调用 close(1000, 'user-left'),并在页面卸载后停止重连;如果是 1006,则把它当作需要重新建立连接的传输异常,同时保留一次可检索的客户端诊断记录。这样既能恢复连接,也不会把所有断开都伪装成正常关闭。
常见问题
1006 能不能由服务端直接发送?
不能。1006 是保留值,服务端不应把它写进 Close 帧;服务端应根据实际协议或业务情况发送允许的关闭码,异常断开则由客户端观察到 1006。
reason 为空是不是服务端没有写日志?
不一定。reason 只来自收到的 Close 帧,没有 Close 帧时为空很正常。服务端日志要和网关、客户端的时间及连接标识一起看。
遇到 1006 只要不断重连就行吗?
不行。先限制退避,再确认是否为代理空闲超时、应用重启、网络切换或协议错误;否则重连可能把故障放大成连接风暴。
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
147 收藏
-
107 收藏
-
221 收藏
-
199 收藏
-
106 收藏
-
483 收藏
-
文章 · 前端 | 1天前 | 前端开发 · 网络请求 · Fetch API · 异步取消 · ReadableStream · Fetch AbortController ReadableStream Response.Body AbortError492 收藏
-
文章 · 前端 | 1天前 | Response · javascript · Fetch API · 异步请求 · 前端排错 · Fetch ReadableStream Response.json bodyUsed 前端请求323 收藏
-
文章 · 前端 | 1天前 | 前端 · Service Worker · 浏览器API · 离线缓存 · 脚本更新 · Service Worker waiting registration.update updatefound installing active394 收藏
-
481 收藏
-
文章 · 前端 | 1天前 | javascript · 前端性能 · IntersectionObserver · 懒加载 · rootMargin · 图片懒加载 IntersectionObserver 前端性能 rootMargin263 收藏
-
178 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习