frame->data为空通常因协议解析未完成或数据不完整,常见于误用onReceive/onPacket回调、处理控制帧、未启用zlib导致解压失败等场景。

frame->data 为空的常见触发场景
Swoole 的 frame->data 为空,绝大多数时候不是 bug,而是协议解析阶段尚未完成或数据未完整到达。典型发生在使用 WebSocket::onMessage 以外的底层回调(比如 onReceive、onPacket)中直接访问 frame->data,或误把 WebSocket\Frame 对象当成普通字符串处理。
- 使用
onReceive回调时,$frame实际是string类型,根本不存在->data属性,强行访问会报Trying to get property 'data' of non-object - 在
onMessage中拿到的$frame是WebSocket\Frame实例,但若客户端发的是控制帧(如 ping/pong/close),$frame->data就是空字符串或null - 启用了
websocket.compress = 1但服务端未启用 zlib 支持,可能导致解压失败,frame->data被置空
怎么确认你拿到的是真正的 WebSocket 数据帧
关键看回调入口和帧类型。只有 onMessage 回调里的 $frame 才是完整的 WebSocket\Frame 对象,且需显式判断 $frame->opcode:
$server->on('message', function ($server, $frame) {
if ($frame->opcode == WEBSOCKET_OPCODE_TEXT) {
// 此时 $frame->data 是 string,可直接用
var_dump($frame->data);
} elseif ($frame->opcode == WEBSOCKET_OPCODE_BINARY) {
// $frame->data 是二进制 string,注意不要 echo 或 json_encode
} else {
// ping/pong/close 帧:$frame->data 为空,忽略或响应即可
}
});
- 不要在
onOpen或onClose里尝试读$frame->data—— 这两个回调根本没有$frame参数 - 若用
onPacket(UDP)或onReceive(TCP),收到的是裸数据,$data是第一个参数,不是$frame->data
抓包验证 frame->data 是否真为空
别只信日志或 var_dump,用 tcpdump 或 Wireshark 看原始 WebSocket payload:
连接建立后,客户端发的首帧必须是 TEXT/BINARY opcode,FIN=1,Mask=1
如果抓包看到 Frame Payload Length 是 0,说明客户端确实发了空消息(合法)
如果抓包显示有数据,但
$frame->data为空,大概率是 Swoole 版本兼容问题:v4.8.0+ 默认启用websocket.fragmentation,小包可能被合并或拆分,需检查$frame->finish和$frame->mask检查 Swoole 版本:
php --ri swoole | grep "Version"临时关闭分片调试:
$server->set(['websocket.fragmentation' => false]);
Swoole 6.1.1下载Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
容易被忽略的初始化配置影响
frame->data 为空有时和 server 启动参数强相关:
websocket.close_on_idle = 1(默认开启)会导致空闲连接被静默关闭,后续帧收不到open_websocket_protocol = false会禁用 WebSocket 协议栈,此时onMessage根本不会触发,$frame无从谈起使用
HttpServer但没调用$http->upgrade(),客户端虽然连上,实际走的是 HTTP 长连接,不是 WebSocket,onMessage不会执行检查
onMessage是否注册成功:var_dump($server->listeners);看回调是否挂载测试时用
wscat -c ws://127.0.0.1:9501发消息,避免前端 JS WebSocket 库自动发 ping 干扰判断
真正麻烦的情况是分片帧(fragmented frame)跨多个 onMessage 到达,$frame->data 非空但不完整——这时得自己缓存并拼接,Swoole 不自动处理。

















