Swoole自定义协议解析只有两条正路:open_eof_check(EOF结束符协议)和open_length_check(固定包头长度协议);其余在on('receive')中手动拼包属权宜之计,非真正协议解析。

直接说结论:Swoole 自定义协议解析只有两条正路——open_eof_check 和 open_length_check,其余所谓“手动拼包+on('receive')里解析”是权宜之计,不是协议解析,是自己扛粘包。
为什么不能只靠 on('receive') 处理粘包
常见错误现象:客户端连续发两段数据,on('receive') 只触发一次,$data 里混着两个包;或者一个大包被拆成两次回调,$data 各拿一半。这不是 Bug,是 TCP 本来就这样。
问题根源在于 Swoole 默认不维护接收缓冲区,on('receive') 是纯透传,收多少给多少。你若在里面做字符串查找、substr() 拆分、状态机缓存,等于把协议解析逻辑全写在 PHP 层——性能差、易出错、UDP/WebSocket 不兼容、没法复用。
真正该做的,是让 Swoole 在 C 层就完成边界识别,只把完整包交给 PHP 回调。这就必须启用内置协议解析开关:
-
open_eof_check => true:靠结束符切包(适合文本协议) -
open_length_check => true:靠包头长度字段切包(适合二进制协议) - 二者不能同时开启,否则配置冲突静默失效
EOF 协议怎么配才不丢数据
关键不是选什么符号,而是客户端和服务端对 package_eof 的理解必须完全一致,且该符号绝不能出现在业务数据中。
典型误用:
- 设
package_eof => "\n",但客户端发的是"hello\r\n"→ 匹配失败,整条消息卡住 - 设
package_eof => "\r\n\r\n",但没配package_max_length→ 长连接下缓冲区无限增长,OOM - 用中文标点如
"|"当 EOF → UTF-8 编码下占多个字节,Swoole 只按字节匹配,极易漏判
实操建议:
- 优先选 ASCII 控制字符,比如
"\0"或"\x04"(EOT),几乎不会撞业务数据 - 务必设
package_max_length(如8192),防止恶意或异常数据撑爆内存 - 客户端发包时,必须严格追加 EOF,PHP 可写
$client->send("data" . "\0")
Length 协议 package_length_type 填 N 还是 V
填错 package_length_type 是最隐蔽的坑——不报错、不警告、连接照常,但永远收不到完整包。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
原因:它控制长度字段的字节序读取方式。C/C++ 客户端普遍用 htonl() 写 4 字节长度,即大端(network byte order),对应 'N';如果客户端用 pack('V', $len)(小端),服务端就得配 'V'。
Wireshark 验证法:抓包看前 4 字节是不是 00 00 00 xx 形式(大端),还是 xx 00 00 00(小端)。
必须配齐的字段(缺一不可):
open_length_check => true-
package_length_type => 'N'(或'V',依客户端而定) -
package_length_offset => 0(长度字段从包首开始) -
package_body_offset => 4(长度占 4 字节,包体从第 5 字节起) package_max_length => 8192
注意:package_length_type 只接受 c/C/s/S/l/L/N/V,填 int32 或 uint32 会静默忽略整个 length check 配置。
UDP 场景下 setProtocol() 完全无效
很多人试图在 UDP Server 上也配 package_length_type,结果发现不起作用——不是配置错了,是根本没走那套逻辑。
原因:UDP 是无连接、无顺序、单包独立的传输层协议,不存在“粘包”概念,也无需“拆包”。Swoole 对 UDP 的 on('packet') 回调就是每收到一个 UDP 报文就触发一次,$data 就是完整一包。
所以:
- UDP 不需要、也不能用
setProtocol()或open_length_check - UDP 自定义协议 = 在
on('packet')里自己解析二进制结构,比如用unpack('Nlen/a*body', $data) - 别指望 Swoole 帮你缓存、拼包、校验重传——那是应用层该干的事
最容易被忽略的一点:同一个 Swoole Server 实例监听 TCP 和 UDP 端口时,TCP 端口可以开 length check,UDP 端口必须关掉所有包解析配置,否则配置会污染或误导自己。

















