必须实现协议解析逻辑处理tcp粘包拆包,需在protocols/myproto.php中声明namespace protocols;,input方法返回包长或0,decode解析数据,encode序列化,worker构造时传入完整fqcn。

要在 Workerman 4.0.20 中让服务端正确识别并处理客户端发来的私有数据格式,必须实现一套能应对 TCP 粘包、拆包、边界判定和结构转换的协议解析逻辑,不能仅靠 onMessage 回调里手动 substr 或 json_decode 应付。
创建协议类文件并声明命名空间
在项目根目录下新建 Protocols/MyProto.php 文件(路径必须是 Protocols/xxx.php,否则 Workerman 找不到),开头写入标准命名空间:namespace Protocols;
这个命名空间是硬性约定,Workerman 会按此路径自动加载协议类;如果写成 AppProtocols 或漏掉 Protocols,框架完全不会调用你的 input/decode/encode 方法。
实现 input 方法:精准判断数据包边界
input 方法决定 Workerman 何时截取缓冲区中的一段完整数据交给 decode。它只接收一个参数 $buffer,返回值必须是整数:
方法一:换行符分隔(适用于 JSON 文本协议)
直接用 strpos($buffer, "
") 查找第一个换行位置;若没找到就返回 0,让 Workerman 继续攒数据;若找到,返回 【$pos + 1】 —— 多加的 1 是为了把换行符本身包含进包长,否则 decode 时会残留 "
" 导致 json_decode 失败。
方法二:固定包头 + 动态体长(推荐用于二进制协议)
先检查 strlen($buffer) (假设包头为 4 字节长度 + 1 字节类型);不足则返回 0;足够则用 <code>unpack('Nlen/Ctype', substr($buffer, 0, 5)) 解出真实 body 长度;此时需返回 【5 + $len】,即包头长度加包体长度,一步到位告诉 Workerman 截多少字节。
注意:返回负数(如 -1)会强制断开该连接,调试阶段慎用;返回 false 会导致连接立即关闭且无日志提示,极难排查。
实现 decode 方法:将原始字节转为业务可用数据
当 input 返回的长度被满足后,Workerman 会把对应长度的原始字节传给 decode。这一步必须做干净的数据清洗和结构转换:
一款AI工具,主要用于在主代理响应前,并行运行Kimi K2.5和GPT 5.3 Codex,注入双方观点以增强认知多样性,适合需要提升相关任务效率的用户。
若 input 用的是换行分隔,decode 就该用 trim($buffer) 去掉首尾空白和换行,再 json_decode($buffer, true) 转数组;不 trim 直接 json_decode 会因末尾 "
" 报错。
若 input 用的是二进制包头,decode 就该用 substr($buffer, 5) 切掉包头,剩下纯 body 交给业务逻辑;切错位置(比如写成 substr($buffer, 4))会导致 body 缺 1 字节或混入类型字段,后续解析全乱。
实现 encode 方法:确保发送数据可被客户端准确还原
encode 是 decode 的逆过程,唯一目标是让客户端能用相同规则还原数据。它接收业务层传来的任意 PHP 变量(数组、字符串、数字等),必须返回 string 类型的二进制流:
第一步:对输入做类型校验,if (!is_array($data) && !is_string($data)) { $data = (string)$data; },避免 json_encode 处理 null 或 resource 出错。
第二步:若协议约定为 JSON + 换行,直接 return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "
"; —— 【JSON_UNESCAPED_UNICODE 必须加】,否则中文变成 uXXXX,客户端解析失败。
第三步:若协议为二进制,先 pack 包头:$body = json_encode($data); $header = pack('Nc', strlen($body), 0x03); return $header . $body;,顺序不能颠倒,否则客户端 unpack 时长度字段读错。
在 Worker 中启用自定义协议
① 确保协议类文件已放在 Protocols/MyProto.php 路径下,且类名与文件名一致(如 class MyProto);
② 创建 Worker 实例时,协议名写成完整类名前缀:`new Worker('tcp://0.0.0.0:8000', ['protocol' => ProtocolsMyProto::class]);`
③ 不要写成 `'protocol' => 'MyProto'` 或 `'protocol' => 'Protocols\MyProto'`,Workerman 4.0.20 严格要求传递 【类的完整 FQCN(带命名空间的类名)】,字符串形式会被静默忽略,降级为默认 text 协议。










