WebSocket发送数据前必须检查readyState为OPEN,封装safeSend函数;捕获onerror和onclose并依据event.code判断重连;确保数据类型匹配服务端要求;手动实现心跳保活;readyState是瞬时状态,需多层兜底。
websocket连接后无法发送数据,90%不是网络或服务端问题,而是客户端没做状态校验或错误未捕获。
检查 socket.readyState 是否为 1(OPEN)再发数据
很多开发者在 onopen 回调里直接发一次数据,之后就不管状态了——但网络抖动、代理中断、服务端主动断连都会让 readyState 变成 0(CONNECTING)、2(CLOSING)或 3(CLOSED),此时调用 send() 不报错,但数据根本不会发出。
- 永远不要在未检查状态时调用
socket.send();必须写成if (socket.readyState === WebSocket.OPEN) { socket.send(...) } - 封装一个安全发送函数,避免重复判断:
function safeSend(socket, data) { if (socket.readyState === WebSocket.OPEN) { socket.send(data); } else { console.warn('skip send: socket not open', socket.readyState); } } - 注意:
WebSocket.OPEN是常量值1,但建议用常量而非硬编码数字,提高可读性
捕获 onerror 和 onclose 并区分异常类型
onerror 是连接阶段失败的兜底入口,但不包含具体原因;真正能拿到错误码的是 onclose 的 event.code。两者必须配合使用,否则重连逻辑会盲目触发。
-
onerror触发时,event对象通常为空或只有type字段,不能依赖它判断是否该重连 -
onclose的event.code才是关键:1000(正常关闭)不重连,1006(异常断连)、1011(服务端内部错误)才应触发重连 - 不要在
onerror里直接调用socket.close()—— 若连接还没建立成功,socket可能处于CONNECTING状态,此时close()会抛InvalidStateError
发送前确认数据类型与服务端预期一致
WebSocket 协议本身不定义消息语义,send() 接收 string、ArrayBuffer、Blob 或 ArrayBufferView,但服务端解析逻辑往往只支持其中一种。类型错配会导致静默丢弃或连接被强制关闭。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 发送 JSON 必须先
JSON.stringify(),不能直接send({ type: 'msg' }) - 若服务端要求二进制帧(如 Protobuf),客户端必须构造
ArrayBuffer并传入send(),不能传字符串 - 发送非 UTF-8 字符串(如 GBK 编码文本)会触发协议级错误,导致连接关闭,浏览器控制台显示
Failed to execute 'send' on 'WebSocket': InvalidAccessError
心跳保活缺失导致“看似连接成功,实则发不出数据”
很多中间设备(Nginx、ALB、CDN)默认 60 秒无流量就回收 TCP 连接。WebSocket 虽有 Ping/Pong 帧机制,但浏览器原生 API 不自动发送,必须手动实现。
- 服务端未开启心跳时,客户端需每 45 秒左右发一次
socket.send('ping')(内容按约定,如纯字符串'ping') - 收到服务端回的
'pong'后重置本地心跳计时器;超时未收到则主动close()并触发重连 - 避免用
setInterval无条件发 ping:如果连接已断开,send()会静默失败,且可能堆积定时器
最易被忽略的一点:readyState 是瞬时快照,不是锁。即使刚检查完是 OPEN,调用 send() 的瞬间也可能因网络抖动变为 CLOSED。所以生产环境必须结合 onerror + onclose + 发送失败回调(如有)三层兜底,不能只信一次状态检查。

















