ws模块需手动管理连接状态,必须检查readystate===websocket.open才能安全通信,广播前要过滤非open客户端,message默认为buffer需转字符串再解析,须处理error和心跳保活。

ws 模块不是“配置完就能用”的黑盒,它暴露的是底层连接对象,直接操作 ws 实例时,稍不注意就会遇到 WebSocket is not open、消息收不到、连接数不准、广播漏发等问题。核心在于理解它的事件生命周期和状态管理。
connection 回调里必须检查 ws.readyState
很多新手在 wss.on('connection', ws => {...}) 里一上来就 ws.send(),结果报错或静默失败。因为连接刚建立时,ws.readyState 可能还是 WebSocket.CONNECTING(尤其在高并发或网络延迟下)。
-
ws.readyState === WebSocket.OPEN才是安全发送的唯一判断依据 - 服务端主动推送(如欢迎消息)建议包一层检查:
if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'welcome', time: Date.now() })); } - 不要依赖
'open'事件——ws实例本身没有该事件,那是浏览器WebSocket对象才有的;Node.js 的ws库只在connection回调中提供已握手完成的实例,但状态仍需手动确认
广播消息时遍历 wss.clients 要过滤掉非 OPEN 状态
直接写 wss.clients.forEach(client => client.send(...)) 是常见错误。客户端可能已断开但尚未触发 'close' 事件(如网络闪断、强制 kill 进程),此时 client.readyState 为 CLOSING 或 CLOSED,调用 send() 会抛异常并中断循环。
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 务必加状态判断:
wss.clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(payload); } }); - 如果用
Map自行维护 clients(比如带元数据),记得在ws.on('close')和ws.on('error')中同步清理,否则内存泄漏+广播错发 -
wss.clients是一个Set,不是数组,不能用map/filter链式调用,要先转成数组再处理
ws.on('message') 收到的 message 默认是 Buffer
除非你明确设置了 binaryType: 'nodebuffer' 或 'arraybuffer',否则传入的 message 类型取决于内容:文本消息是 Buffer,二进制消息也是 Buffer。直接 message.toString() 可能乱码,JSON.parse(message) 会报错。
- 最稳妥做法:
ws.on('message', (message) => { const data = message instanceof Buffer ? message.toString() : message; try { const parsed = JSON.parse(data); // 处理逻辑 } catch (e) { console.warn('无效 JSON:', data.slice(0, 100)); } }); - 如果确定只收文本,可在连接时设
ws.binaryType = 'nodebuffer',但没必要——toString()已足够 - 别在
message上直接调用.length判断大小,Buffer 的.length是字节数,字符串的.length是字符数,混淆会导致截断或超限误判
生产环境必须处理 ws.on('error') 和心跳保活
WebSocket 连接不像 HTTP 请求有明确超时,NAT、代理、防火墙会在空闲几秒后单向断连,客户端无感知,服务端还留着这个 ws 实例,导致后续 send() 报错或静默丢弃。
- 必须监听
error事件,否则未捕获异常会让进程 crash:ws.on('error', (err) => { console.error('WebSocket error:', err.message); // 通常应在此 close 连接 ws.terminate(); }); - 实现简单心跳:服务端每 30s 发
ws.ping(),客户端响应pong;同时监听ws.on('pong')重置超时计时器 - 不要用
setInterval直接发 ping —— 每个连接应独立管理心跳定时器,避免一个连接卡住拖垮全部
wss 实例本身不自动清理失效连接,所有状态校验、错误恢复、资源释放都得你亲手写。看似几行代码能跑通 demo,但真正扛住几十个并发、持续运行一周不掉线的逻辑,全藏在这些细节点里。










