workerman解析tcp二进制数据核心是手动用unpack()/pack()配合字节偏移与缓冲管理,而非框架自动解析;必须严格对齐字节序(如n大端、v小端)、字段长度及粘包处理,否则解析失败。

Workerman TCP 处理二进制数据,核心不是“框架帮你解析”,而是你得用 PHP 原生的 unpack()、pack() 和字节偏移控制来手动拆解/构造。框架只管收发 $data(就是一串原始 string),剩下的全靠你写逻辑。
怎么用 unpack() 正确读取大端/小端字段
二进制协议里字段顺序、字节序、长度都得和客户端(比如 C/C++/Go 程序)严格对齐,错一个格式符就全乱。
-
N读 4 字节大端整数(如0x12345678→305419896),V是小端对应;别混用L(小端)或l(有符号小端) - 字符串字段要按实际长度截取,比如协议定义“用户名 16 字节”,就得先用
substr($data, 8, 16)提出来,再用rtrim($str, "\x00")去掉末尾空字节 - 浮点数用
g(大端 IEEE 754 单精度)或e(小端),不能直接用f(平台相关,默认可能小端但不可靠) - 示例:包头 2 字节长度 + 4 字节命令 ID + 16 字节用户名,大端协议下解析:
unpack('nlen/Ncmd/a16username', $data)
为什么 onMessage 收到的数据经常不完整或粘包
TCP 是流式协议,$data 不等于一个“消息”,它可能是半包、整包、两包拼一起,甚至跨多个 onMessage 调用到达。
- 不能假设每次
$data都是完整结构体 —— 必须自己实现缓冲和分包逻辑 - 最简方案:在自定义协议类中实现
input()方法,返回当前缓冲区里第一个完整包的长度;Workerman 会等够了字节才调用decode() - 常见错误:直接对
$data调unpack(),结果因长度不足触发unpack()返回false或解析出错值 - 如果不用自定义协议,就得在
onMessage里维护连接级缓冲:$connection->tmp_buffer .= $data,然后循环检查并截取完整包
如何安全地把 PHP 数据打包成二进制发出去
pack() 的格式符必须和接收方约定一致,尤其注意大小端和填充方式。
- 发送大端整数用
pack('N', $id),小端用pack('V', $id);别用L(小端)除非确认对方也用小端 - 字符串要定长填充:
str_pad(substr($name, 0, 16), 16, "\x00"),再传给pack('a16', ...) - 结构体有变长字段(比如最后跟一个 UTF-8 字符串)时,必须先写长度字段(如
pack('n', strlen($body))),再写内容,接收方才能正确截取 - 避免隐式类型转换:
pack('N', (int)$value)显式转整型,防止 float 被截断
真正容易被忽略的是字节序一致性与缓冲管理的耦合 —— 很多人调通了单次通信就以为没问题,但高并发下粘包逻辑一旦有竞态或清缓存不及时,就会出现偶发解析失败,且难以复现。务必在连接对象上绑定缓冲区,并在 onClose 里清理。











