workerman 5.2.3 处理 tcp 粘包拆包的核心是识别消息边界,需通过“长度前缀+数据体”协议在应用层主动解析:input() 方法根据前4字节长度字段判断是否收到完整包,返回对应字节数或0,封包解包须严格对称并统一用大端序。

Workerman 5.2.3 处理 TCP 粘包拆包,核心不是“避免”,而是“识别边界”——因为粘包拆包是 TCP 字节流的天然特性,无法绕过,只能在应用层主动定义和解析消息边界。自定义协议的关键,在于让每条业务消息自带可识别的长度或结构,使 Workerman 能按需截取完整帧。
协议设计:必须带长度字段(推荐)
最稳定、跨语言兼容性最强的方式是“长度前缀 + 数据体”格式,例如:
- 前 4 字节为 body 长度(网络字节序,即大端),用
pack('N', $len)封包; - 紧接其后是原始业务数据(如 JSON 字符串、二进制结构等);
- 不依赖分隔符(如
\n),避免业务数据含该字符导致解析失败; - 不采用固定长度,避免小消息浪费、大消息溢出或截断。
实现 ProtocolInterface:input() 是关键
Workerman 通过 ProtocolInterface::input() 方法预处理接收缓冲区,决定当前是否已收到完整包。它返回三种值:
- 0:数据不足,继续等待;
-
正整数 N:表示前 N 字节构成一个完整包,Workerman 自动截取并传给
onMessage; - false:协议错误,直接断开连接。
典型实现逻辑:
先检查缓冲区是否有至少 4 字节 → 解出 body 长度 → 再检查剩余缓冲区是否 ≥ 该长度 → 满足则返回总长度(4 + body_len),否则返回 0。封包与解包要对称
客户端和服务端必须严格遵循同一规则:
-
encode():发送前,把业务数据
$data加上 4 字节长度头,返回pack('N', strlen($data)) . $data; -
decode():通常可留空(因
input()已完成拆包),或仅做简单校验(如 JSON 解析); - 务必统一字节序,PHP 默认
pack('N')是大端,C#/Java/Go 客户端也需用大端读取长度。
防坑提醒:几个易错点
实际调试中高频出问题的地方:
- 客户端未按协议封包(比如漏了长度头,或用了小端)→ 服务端
input()一直返回 0,收不到任何消息; - 长度字段本身被截断(如只收到前 2 字节)→
unpack('N', ...)解出错误值,后续彻底错乱; - 缓冲区未及时清理:每次成功解析后,
$buffer必须用substr()截掉已处理部分,否则残留数据干扰下一次解析; - 心跳或空包未约定:若允许发空消息(如纯心跳),需明确定义长度为 0 的合法行为,并在
input()中支持。











