workerman自定义协议数据截断主因是input未准确返回包长;须确保input精确返回含头+体的总字节数,不足时返0,decode中严禁未校验长度就substr。

Workerman 5.2.3 自定义协议出现数据截断,本质是 input 方法未能正确识别完整包长,导致 decode 只拿到部分数据,后续字节被丢弃或滞留在缓冲区。这不是 Bug,而是协议实现未严格匹配 TCP 流式特性的典型表现。
检查 input 方法是否准确返回包长度
这是最常见原因。input 必须精确返回一个完整数据包的总字节数(含包头+包体),不能多也不能少。
- 若协议是「4 字节长度 + N 字节内容」格式,input 必须先确认缓冲区至少有 4 字节;再用 unpack('N', $buffer) 解出真实 body 长度 L;最后确认缓冲区总长 ≥ 4 + L —— 满足才返回 4 + L,否则返回 0
- 返回值为 0:Workerman 继续收包,不触发 decode
- 返回值为正整数 N:Workerman 截取前 N 字节传给 decode,剩余字节保留在缓冲区等待下一次 input
- 返回 false:直接关闭连接(慎用)
避免 decode 中修改原始 buffer 或提前截断
decode 接收的是 input 返回长度后截出的“完整包”,它只负责解析,不能重新切片、substr 错位、或依赖全局 buffer 状态。
- 错误写法:
substr($data, 4)却没校验 $data 长度 ≥ 4 → 导致 warning + 截断 - 正确做法:先判断
strlen($data) 直接 throw 或 return null(但 input 已保证完整性,此处应为防御性检查) - decode 返回值必须是应用层可处理的数据(如数组、字符串、对象),不能是二进制片段
排查协议头解析逻辑是否支持多包连续到达
TCP 流中可能一次收到多个完整包(例如客户端 burst 发送),input 必须支持循环解析,不能只处理第一个包就退出。
- 在 input 内部用 while 循环反复检查缓冲区,每次解析出一个包长并累加到返回值,直到无法构成完整包为止
- 参考状态机写法:维护 parser 实例 per-connection(非静态),在 onMessage 前完成多次解包,确保所有完整包都被提取
- 不要在 input 中做 unpack 后的业务逻辑(如类型判断、日志打印),那属于 decode 职责
验证客户端打包是否与服务端严格对齐
截断常是“一果两因”:服务端 input 认为包不完整,而客户端 encode 实际已发全。
- 用 tcpdump 或 Wireshark 抓包,确认发送端是否真发出了预期字节数(例如 header=4 + body=100 → 总长 104)
- 检查 encode 是否用了 pack() / unpack() 字节序(如 'N' 是大端,'V' 是小端),客户端 C# 的 BitConverter.GetBytes(len) 默认小端,PHP 应用 'V' 匹配
- 禁用 Nagle 算法(
$connection->tcpNoDelay = true;)可减少小包合并,便于调试











