package_length_type 支持三种字节序(网络序/大端、主机序、小端)和两种符号类型(有符号、无符号),共10种组合:c/c(1字节)、s/s/n/n/v/v/l/l(2或4字节),其中n/n/v/v/l/l均为无符号,c/s/l为有符号。

package_length_type 支持哪些字节序和符号类型
package_length_type 是 Swoole 底层解析包长度字段时用的 unpack 规则,本质就是 PHP unpack() 的格式字符。它不负责编码,只告诉 Swoole “这个长度字段占几个字节、有没有符号、用什么字节序”。选错会导致解出的长度值完全错误,比如本该是 1024 却解成 -256 或 16777216,后续直接触发 package_max_length 截断或内存溢出。
目前支持 10 种,核心差异在三方面:符号性(有/无符号)、字节数(1/2/4)、字节序(网络序/主机序/小端)。常见组合如下:
-
c/C:1 字节,c带符号(范围 -128~127),C无符号(0~255)——适合极短消息,如状态码 -
s/S:2 字节,主机字节序(即 CPU 当前默认顺序,x86 是小端),s有符号,S无符号 -
n/N:2/4 字节,**网络字节序(大端)**,都无符号——跨平台最稳,推荐用于服务间通信 -
v/V:2/4 字节,**小端字节序**,都无符号——与 x86/x64 主机序一致,但和 Java/Go 默认不兼容 -
l/L:4 字节,主机字节序,l有符号,L无符号——PHPpack("l", 1)在 x86 上和pack("V", 1)结果一样,但语义不同
为什么 N 和 V 经常被混淆
很多人以为 N 和 V 都是“4 字节无符号”,只要长度对就行。实际不是:它们字节排列相反。例如整数 0x12345678:
-
pack("N", 0x12345678)→\x12\x34\x56\x78 -
pack("V", 0x12345678)→\x78\x56\x34\x12
如果客户端用 pack("V", $len) 发包,而 server 配了 'package_length_type' => 'N',Swoole 就会把 \x78\x56\x34\x12 当作大端数解读为 0x78563412(约 20 亿),远超 package_max_length,直接断连或报 ERROR: package is too big。反过来也一样。
所以必须两端严格一致。生产环境强烈建议统一用 N(4 字节大端)或 n(2 字节大端),避免因部署机器 CPU 架构不同(ARM 大端/小端混用)引发隐性故障。
package_length_offset 和 package_body_offset 怎么配合 type 使用
package_length_type 只管“怎么读长度值”,但长度值在哪、长度是否含包头,由另外两个参数决定:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
-
package_length_offset:从包头起第几个字节开始取长度字段,按字节偏移(从 0 开始) -
package_body_offset:长度值所表示的“包体长度”,是从整个数据块的第几个字节开始算起
举个例子:协议头是 type(4) + uid(4) + len(4) + serid(4),共 16 字节,其中 len 字段存的是纯包体长度(不含这 16 字节头):
- 长度字段起始位置是第 8 字节 →
package_length_offset => 8 - 长度值不包含包头 →
package_body_offset => 16 - len 是 4 字节无符号大端 →
package_length_type => 'N'
如果 length 字段本身是 2 字节(比如用了 'n'),但你误配成 'N',Swoole 会多读 2 字节,导致后续所有字段错位——这时收到的 $data 在 onReceive 里开头就乱码,且长度解析永远不对。
实际调试时最容易忽略的一点
PHP 的 pack() 和 Swoole 的 package_length_type 必须一一对应,但很多人只检查了 server 端配置,忘了 client 端 pack() 调用是否匹配。尤其在使用协程客户端 Swoole\Coroutine\Client 或异步 Swoole\Client 时,发包逻辑可能分散在多个函数里,pack 调用容易被遗漏或写错。
最简验证方式:用 bin2hex() 打印出实际发送的前若干字节,对照 pack 规则人工验算。例如:
var_dump(bin2hex(pack('N', 1024))); // string(8) "00000400"
再看抓包工具(如 wireshark)里对应位置是否一致。不亲眼确认字节流,光看配置很容易掉坑里。










