Workerman解析结构体本质是按二进制布局用unpack()拆解字节流,关键在于选对大端格式符(N/n/g)及偏移量,避免误用小端符(L/S/d),并手动处理变长字段与TCP粘包。

Workerman里解析结构体数据,本质是解析二进制协议
Workerman本身不提供结构体解析能力,它只负责收发原始字节流。所谓“解析结构体”,其实是你按某种二进制布局(比如C语言struct定义)把字节拆成字段的过程。关键不是Workerman怎么做,而是你用unpack()时选对格式符、字节序和偏移量。
用unpack()读大端序(Big-Endian)字段必须加N/n/g等大端标识
常见错误是直接用unpack('L', $data)——L是小端32位整数,如果数据来自网络或C服务端(默认大端),结果就错得离谱。TCP/IP协议栈、大多数C服务端、Java/Go默认都用大端序。
-
N:无符号32位整数,大端(最常用,对应C的uint32_t) -
n:无符号16位整数,大端(对应uint16_t) -
g:双精度浮点,大端(IEEE 754,对应double) - 别用
L/S/d——它们是小端,除非你明确知道发送方是x86本地内存直传
例如,一个C结构体:struct { uint16_t len; uint32_t id; char name[32]; },在PHP里解析前6字节就得写:unpack('nlen/Nid', $binary_data),而不是'Slen/Lid'。
小端序数据要小心来源,不能默认用L/S
小端序数据通常只出现在:本地调试时用pack('L', $val)生成的测试数据、Windows上某些旧程序、或明确声明用小端的嵌入式设备。但即使如此,也建议显式标注:
- 用
V代替L(V= 32位小端无符号整数,语义更清晰) - 用
v代替S(v= 16位小端无符号整数) - 避免混用:比如
unpack('Nv', $data)——前4字节大端,后2字节小端,容易看漏
如果你不确定字节序,最稳的办法是拿已知值测试:让发送方发0x00000001,在PHP里用N和V分别unpack,看哪个返回1。
结构体里有字符串或变长字段,得手动切片+计算偏移
unpack()只能处理固定长度字段。一旦结构体含字符串、数组或char buf[]这种柔性数组,就必须先用unpack()读出长度字段,再用substr()截取后续内容。
- 比如头4字节是
msg_len(大端),后面才是JSON体:先$header = unpack('Nlen', $raw);,再$body = substr($raw, 4, $header['len']); - 别试图用
unpack('Nlen/a*body', $raw)——a*会吃掉所有剩余字节,不认长度,粘包时直接崩 - Workerman的
onMessage回调收到的是完整TCP流片段,可能只有一半结构体,必须配合协议类做缓冲(input属性)或自定义decode()方法
真正容易被忽略的点是:字节序错误往往不会报错,只会返回一个莫名其妙的数字(比如本该是100,却得到1677721600),排查时容易绕远路去查网络或Workerman配置,其实问题就在unpack那行格式字符串里。

















