要在Workerman中实现自定义数据包格式,必须绕过默认TCP裸流解析,通过编写Protocol类并正确放置于Protocols目录、设置命名空间为Protocols,实现input(识别包边界)、decode(解析数据)、encode(序列化发送)三个核心方法,再在Worker中指定协议名启用。

要在Workerman中实现自己的数据包格式,必须绕过默认的TCP裸流解析逻辑,让服务端能准确识别每个完整数据包的起始与结束位置,否则会因TCP粘包/拆包导致业务数据错乱、JSON解析失败或连接异常中断。
创建协议类文件并放置到正确路径
在你的项目根目录下,确保存在 Protocols 文件夹(如不存在请手动创建),然后新建一个PHP文件,例如 MyProtocol.php。
文件必须放在 Protocols/MyProtocol.php 路径下,且命名空间必须为 Protocols;若路径或命名空间错误,Workerman启动时会直接报 【Class 'Protocols\MyProtocol' not found】 致命错误,无法加载协议。
用文本编辑器打开该文件,写入标准协议类骨架:
实现input方法:决定何时算“一包收齐了”
这是最关键的一步。input方法接收原始字节流 $buffer,返回值决定Workerman是否触发decode和onMessage。
方法一:用换行符 \n 作为包边界(适合纯文本/JSON场景)
在类中添加 static function input($buffer) {
$pos = strpos($buffer, "\n");
if ($pos === false) return 0;
return $pos + 1;
}
方法二:用前4字节表示包体长度(兼容C#/Java客户端)
先检查缓冲区是否至少有4字节:
if (strlen($buffer)
再用 unpack('Nlen', $buffer) 提取大端序整数长度 len;
若 len ≤ 0 或 strlen($buffer)
否则返回 4 + len —— 这个值必须严格等于实际截取长度,否则 decode 会收到错位数据。
⚠️ 注意:input 返回 0 表示继续等待,返回正整数表示立即截取对应长度交给 decode,返回 false 会直接断开该客户端连接。
实现decode方法:把原始字节转成PHP可用数据
当 input 返回正值后,Workerman会把对应长度的字节传给 decode 方法。
如果 input 用的是 \n 分隔,decode 就要移除末尾换行并 JSON 解码:
return json_decode(trim($buffer, "\n"), true);
如果 input 用的是长度前缀,decode 就要跳过前4字节再处理:
$body = substr($buffer, 4);
return json_decode($body, true) ?: [];
这一步不能抛出异常,否则 onMessage 不会被调用;若解码失败,建议返回空数组或 null,由业务逻辑判断。
实现encode方法:把PHP数据转成网络可传格式
当调用 $connection->send($data) 时,Workerman自动将 $data 传入 encode 方法处理后再发出去。
对应 \n 协议:
return json_encode($data, JSON_UNESCAPED_UNICODE) . "\n";
对应长度前缀协议:
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
$len = strlen($json);
return pack('N', $len) . $json;
注意:encode 输出必须与 input 的解析规则严格匹配,否则客户端永远收不到完整包。
在Worker中启用自定义协议
第一步:引入 Workerman 自动加载器
require_once __DIR__ . '/vendor/workerman/workerman/Autoloader.php';
第二步:实例化 Worker 并指定协议名
$worker = new Worker('MyProtocol://0.0.0.0:2346');
第三步:设置 onMessage 回调
$worker->onMessage = function($connection, $data) {
var_dump($data); // 此处 $data 已是 decode 后的数组,非原始字符串
$connection->send(['status' => 'ok']);
};
第四步:启动服务
Worker::runAll();

















