onmessage不处理ping帧是正常行为,因其opcode为9,仅处理opcode1(text)和2(binary);应用层心跳需用json字符串而非协议ping帧。

ping帧被Swoole底层自动处理,不会进入onMessage
Swoole WebSocket服务器收到客户端发来的 ping 帧时,**根本不会调用 onMessage 回调**——这是协议层的硬性设计,不是配置错误或漏写回调。RFC 6455 明确要求服务端必须自动响应 pong,且不得将 ping/pong 视为业务消息分发。
- 你看到连接正常、
onOpen触发了,但发ping后onMessage完全没日志?这是预期行为,不是 bug -
onMessage只处理opcode为1(text)或2(binary)的数据帧,ping的 opcode 是9,pong是10,早被底层截断 - 想确认是否收到 ping?只能靠抓包(如 Wireshark 或 Chrome DevTools → WS → Frames)看 Type 列是否出现
ping
需要自定义心跳逻辑?别依赖 ping/pong 帧类型
如果你的业务要求“客户端发 {"type":"ping"} 字符串,服务端回 {"type":"pong"}”,那这不是 WebSocket 协议 ping,而是**应用层心跳消息**,必须走 onMessage 处理。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 前端不要调用
socket.ping(),改用socket.send('{"type":"ping"}') - 服务端在
onMessage中判断:if (is_string($frame->data) && ($data = json_decode($frame->data, true)) && $data['type'] === 'ping') { $server->push($frame->fd, '{"type":"pong"}'); } - 注意:Swoole 不会帮你解析 JSON,
$frame->data是原始字符串,json_decode失败要兜底,否则整条消息静默丢弃 - 别把应用心跳和协议 ping 混在一起——Nginx 默认 60 秒切断空闲连接,而协议 ping 间隔若设为 55 秒,客户端可能因收不到 pong 而误判断线
onMessage 不触发的真正常见原因,往往不是 ping
很多人排查时盯着 ping,结果卡住的是更基础的问题:类型不匹配、帧未解码、Nginx 拦截。
- 前端用
socket.send(new ArrayBuffer())发二进制,但 PHP 服务端只写了onMessage接收字符串?直接跳过,不报错也不进回调 - PHP 原生 socket 手写服务,
socket_read()拿到的是带 mask 的原始帧,json_decode()必然失败——必须用ratchet或workerman等成熟库解帧 - Nginx 配置漏了
proxy_set_header Upgrade $http_upgrade和Connection "upgrade",导致 WebSocket 握手降级成 HTTP,后续所有帧都变成 400 或静默丢弃 - TP8.0 下中间件对 WebSocket 消息做 JSON 解析,但传入的是二进制帧,中间件静默返回空,
onMessage根本收不到
调试时最容易忽略的一点
Chrome DevTools 的 WS → Frames 标签页里,Type 列显示的是 text 还是 binary,这个信息比服务端日志还真实。如果这里显示的是 ping,你就不用再翻 onMessage 代码了——它本来就不该触发。真正该查的,是你的业务消息到底有没有发成 text 帧,以及 Nginx 或 TLS 层有没有悄悄把它吞掉。










