WebSocket收不到消息主因是消息被过滤、帧未解码或中间件静默断开;需检查前端发送帧类型(Binary/Text)、后端@OnMessage是否匹配、Nginx/Spring Gateway空闲超时配置及原生socket是否解析WebSocket帧。

WebSocket连接成功但收不到消息,90%不是连不上,而是“消息被过滤”“帧没解码”或“连接早被中间件静默断开”——先别改网络配置,从这三处查起。
前端发的是 BinaryMessage,但后端 @OnMessage 只监听 TextMessage
Java 的 @OnMessage 默认只响应 TextMessage。如果前端调用 socket.send(new ArrayBuffer()) 或 socket.send(blob),哪怕内容是合法 JSON,后端 @OnMessage 方法也完全不会触发,日志静默,无报错。
- 在浏览器开发者工具的 Network → WS → Frames 标签页里,直接看发送帧类型是「Text」还是「Binary」——这是最准的判断依据
- 后端必须显式添加二进制接收方法:
@OnMessage接收byte[]或ByteBuffer,再手动转成字符串:new String(message, StandardCharsets.UTF_8) - 前端若不确定类型,统一用
typeof data === 'string'判断后再发;否则加个包装层,强制socket.send(JSON.stringify(data))
Nginx / Spring Gateway 等中间件空闲超时,连接“假活”
连接状态仍是 OPEN,但 Nginx、Spring Gateway 或云 WAF 已在后台发 FIN 关闭了底层 TCP 连接。后续消息被静默丢弃,socket.send() 不报错,onmessage 也不触发。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 默认空闲超时多为 60 秒,必须在
onopen后立即启动心跳:每 30 秒调一次socket.send('ping')(内容任意,但不能为空) - Spring 服务端需确认
ReactorNettyWebSocketService的maxFramePayloadLength和空闲超时已调大,否则心跳可能被截断 - 若用 Nginx 代理 WebSocket,检查配置中是否有
proxy_read_timeout 300;和proxy_send_timeout 300;,不能只靠默认值
PHP/Node 原生 socket 拿到的是原始掩码帧,不是 UTF-8 文本
握手成功 ≠ 能直接 socket_read($client, 1024) 得到明文。客户端发来的所有帧都带掩码、长度头和操作码,原生 socket 读到的是二进制乱码,不是 JSON 字符串,更不是可直接 json_decode() 的内容。
- PHP 必须实现 WebSocket 帧解析逻辑:校验 FIN、opcode、mask、payload length,并对 payload 异或解码(mask key 是 4 字节)
- Node.js 若用
net.Socket而非ws库,同样要自己解析帧结构;否则data.toString()输出的是不可读字节流 - 简单验证方式:用
wscat -c ws://your-host发送纯文本,对比原生 socket 读出的十六进制输出是否含0x81(text opcode)和有效 UTF-8 字节序列
真正难排查的从来不是“连不上”,而是“连上了却像没连”——帧类型不匹配、中间件超时、原始帧未解码,这三个点任何一个漏掉,都会让 onmessage 永远沉默。动手前,先打开 Frames 标签页看一眼类型,比翻十遍日志还快。

















