websocket send()失败主因是连接未就绪或已失效,需优先检查readystate;其不抛异常,仅在非open状态静默失败或报invalidstateerror,错误码1006属连接死亡标识而非发送错误。

WebSocket客户端发送消息失败,绝大多数情况不是代码写错了,而是连接本身已失效或未就绪——直接检查 socket.readyState 比抓错误码更有效。
为什么 socket.send() 报错却没看到明确错误码
WebSocket 的 send() 方法本身不抛异常,它只在 socket.readyState !== WebSocket.OPEN 时静默失败(返回 false 或直接报 InvalidStateError)。浏览器控制台常显示:
Failed to execute 'send' on 'WebSocket': Still in CONNECTING state Failed to execute 'send' on 'WebSocket': InvalidStateError: The connection is not open
这类错误根本不会触发 onerror 或产生关闭码,因为连接压根没活过。常见诱因包括:
- 调用
send()太早,onopen还没触发(比如在new WebSocket(url)后立刻发) - 握手被服务端拦截(如 Origin 校验失败、JWT 过期、Nginx 返回 403)
- 网络层阻断导致连接卡在
CONNECTING状态超时(如防火墙丢包、代理不转发 Upgrade)
event.code === 1006 不是发送失败的错误码,而是连接已死的“尸体标签”
1006 出现在 onclose 里,但它和发送消息失败无关——它是客户端发现 TCP 连接没了、又没收到任何关闭帧时自填的占位码。此时你再调 send() 必然失败,但根源不在 send,而在连接早已中断。排查方向应聚焦于:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 是否启用了心跳?30 秒一次
ping能防 NAT/ALB 静默超时 - 服务端日志里有没有
write: broken pipe或 panic 堆栈?1006 往往对应服务端崩溃 - 客户端设备是否切到了飞行模式、锁屏休眠、或浏览器标签页被冻结(尤其 Chrome 后台页)
真正和发送消息相关的错误码只有这几个
能由 send() 行为间接触发、且有明确含义的关闭码极少,重点关注:
-
1002:协议错误。常见于服务端收到非法帧(如非 UTF-8 文本、超长 payload),或客户端误用二进制数据当文本发(没设socket.binaryType = 'arraybuffer') -
1009:消息过大。服务端配置了最大帧长度(如 gorilla/websocket 的WriteBufferSize),而你发了 5MB JSON 却没分片 -
1011:服务器出问题。这是唯一值得盯的服务端主动关闭码——event.reason通常带具体异常(如 “out of memory”),服务端日志必有对应记录
注意:1005 和 1006 在 onclose 中出现时,event.reason 为空字符串,无法提供调试线索。
Go / Java / Node.js 服务端对发送失败的处理差异
不同语言栈对底层 write 错误的封装策略不同,直接影响客户端看到的关闭码:
- Node.js
ws库:未捕获异常 → 自动发1011;手动调socket.close(1002)→ 客户端收1002 - Go
gorilla/websocket:必须显式调c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(...))才会发关闭帧,否则连接断开即得1006 - Java Spring WebSocket:抛
RuntimeException→ 映射为1011;@SendToUser目标离线 → 不报错,消息丢弃
最易被忽略的是:客户端反复重连时,旧连接的 socket 实例可能还在内存里,send() 发到已关闭对象上,既不报错也不送达——务必在 onclose 里清掉定时器、取消 pending send,并置空引用。










