Workerman 5.2.3 实现私有消息协议需定义含 input、decode、encode 静态方法的协议类;采用定长4字节大端包头+JSON载荷格式,解决粘包并透传数组给业务层。

用 Workerman 5.2.3 实现私有消息协议,核心是定义一个符合框架规范的协议类,包含 input、decode、encode 三个静态方法。它不依赖外部扩展,纯 PHP 实现,重点解决 TCP 粘包/拆包,并让业务层收发的是结构化数据(如数组),而非裸字节流。
协议设计:定长包头 + JSON 载荷
推荐一种简洁可靠的设计:前 4 字节为 uint32_t 大端整数,表示后续 JSON 数据长度;之后紧跟 JSON 字符串,末尾无换行或分隔符。例如:
-
\x00\x00\x00\x12{"cmd":"ping","ts":1734892345}(共 4 + 18 = 22 字节)
这种格式明确、无歧义、易调试,也便于 Unity/C# 等客户端对接(用 BitConverter.GetBytes(len).Reverse() 即可生成包头)。
编写协议类(如 Protocols/MyMsg.php)
文件路径需在自动加载范围内(如放在 app/Protocols/MyMsg.php),命名空间为 Protocols,且必须声明为 static 方法:
-
input:检查缓冲区是否含完整包。先读前 4 字节,若不足则返回 0;解析出长度
$len后,判断缓冲区总长是否 ≥4 + $len;是则返回总长度,否则返回 0。 -
decode:截取 payload 部分(跳过 4 字节头),用
json_decode($payload, true)转为数组;失败时可返回null或抛异常,Workerman 会断开连接。 -
encode:接收数组,
json_encode($data, JSON_UNESCAPED_UNICODE)得到字符串,用pack('N', strlen($json))生成大端 4 字节头,拼接后返回二进制字符串。
在 Worker 中启用该协议
创建 TCP Worker 时,将协议类名传入构造函数第二参数:
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:7788', [
'protocol' => \Protocols\MyMsg::class
]);
$worker->onMessage = function($connection, $data) {
// $data 已是解码后的数组,如 ['cmd'=>'ping', 'ts'=>1734892345]
if (isset($data['cmd']) && $data['cmd'] === 'ping') {
$connection->send(['cmd' => 'pong', 'ts' => time()]);
}
};
注意:onMessage 收到的就是 decode 返回的结果;调用 $connection->send($array) 时,框架自动调用 encode 封包。
验证与调试建议
初期可用 telnet 或 nc 手动发送十六进制数据测试分包逻辑:
- 发
\x00\x00\x00\x0a{"a":1}(10 字节 JSON + 4 字节头)→ 应触发一次onMessage - 发
\x00\x00\x00\x0a{"a":1}再紧接\x00\x00\x00\x0b{"b":2}→ 应触发两次onMessage - 只发
\x00\x00→input返回 0,连接保持,等待后续数据
在 input 和 decode 中加 var_dump 或日志,能快速定位粘包处理是否正确。

















