1006是WebSocket异常关闭码,表示TCP连接已断但未发送关闭帧,常见于Nginx超时、手机切后台、心跳失败等场景,需结合网络、代理、心跳及客户端状态综合排查。

WebSocket连接状态码不是HTTP状态码,它只出现在关闭帧(Close Frame)中,且必须是16位无符号整数。真正需要你关注的不是“有哪些”,而是哪些会实际触发、如何区分、以及怎么响应——尤其是1006这种不报错却断连的“幽灵错误”。
WebSocket关闭码1000~1015对应什么行为
这些是RFC 6455定义的标准关闭码,前三位(100x)最常遇到:
-
1000:正常关闭。你调了close(),对方收到了关闭帧,也回了响应,TCP才断。此时event.wasClean === true -
1001:对端离开。比如用户关了标签页、浏览器休眠、服务端进程退出。前端收到时event.code === 1001,但event.wasClean可能为false(取决于是否来得及发帧) -
1002:协议错误。客户端发了非法Opcode(如0x9以外的控制帧),或分片混乱。服务端通常直接断连,不给原因 -
1003:不支持的数据类型。比如后端只收text,你却发了ArrayBuffer,且没设binaryType = 'arraybuffer' -
1006:异常关闭。**没有发送任何关闭帧就断了**——这是最危险的状态码,它本身不说明原因,只告诉你“连接已死”。常见于Nginx超时、手机切后台、Wi-Fi切换、心跳未响应 -
1009:消息太大。服务端配置了maxPayload(如Spring Boot的spring.websocket.max-text-message-payload-size),你发的JSON字符串超长,会被静默截断或直接断连 -
1011:服务端内部错误。比如Java里@MessageExceptionHandler抛出未捕获异常,Spring WebSocket默认返回这个码
为什么1006不能靠onclose里的event.reason定位问题
event.reason在绝大多数场景下是空字符串,包括Nginx代理超时、Chrome标签页冻结、iOS Safari后台节流等情况。它只在主动调用close(code, reason)且reason非空时才有效。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- Chrome DevTools的Network → WS → Frames里,
1006出现前最后一帧往往是Ping,但没收到Pong—— 这说明心跳链路已断 - Wireshark抓包看
TCP RST由哪端发出:server → client发RST,大概率是服务端主动kill socket(如超时清理);client → server发RST,多是JS代码里websocket.close()被异常中断或页面卸载 - Node.js的
ws库里,conn.on('close', code => {...})收到1006时,conn.readyState已经是CLOSED,再调conn.send()会直接throw
前端重连逻辑里最容易忽略的三个条件
光判断event.code === 1006就重连,大概率失败。你还得确认这几件事:
- 检查
websocket.readyState是否为CLOSED或CLOSING,避免重复new WebSocket实例导致连接堆积 - 区分
wasClean === false和code === 1006:前者是浏览器级断连(如关页),后者更可能是网络层问题;但iOS Safari里wasClean经常误报true,所以不能只信它 - 重连前加退避(exponential backoff):第一次等1s,第二次2s,第三次4s……别用
setTimeout(() => connect(), 0),高频建连容易被Nginxlimit_conn拦截
Golang服务端收到ReadMessage返回websocket.CloseAbnormalClosure怎么办
这不是你的bug,是连接已经从TCP层消失了。Golang的ws库不会自动帮你清理资源,必须手动处理:
- 先用
websocket.IsCloseError(err, websocket.CloseAbnormalClosure)确认类型,别直接return或log.Fatal - 立刻停止所有对该
conn的读写操作:不要再调conn.WriteMessage,否则panic;也不要deferconn.Close(),它可能已失效 - 清理关联资源:从
sync.Map中Delete该连接、cancel其context、close通知channel - 如果要主动关闭并让对端收到
1000,必须先发conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "ok")),再conn.Close();写失败就跳过,直接conn.Close()
真正难处理的从来不是1000或1001,而是1006背后那一长串中间件策略、心跳间隔、客户端生命周期管理的耦合。它不报错,却让重连逻辑失效;它不提示,却要求你同时懂浏览器、代理、服务端和网络栈。盯住1006出现时的上下文,比背熟所有状态码有用得多。


















