必须实现 protocolinterface,因为 hyperf server 启动时依赖该接口的 pack/unpack 等方法处理连接生命周期与帧解析;继承 jsonrpcprotocol 会导致强耦合 json-rpc 2.0 结构(如强制校验 jsonrpc 字段),无法适配二进制或自定义文本协议,易抛 invalidpacketexception 或静默丢包。

Hyperf 的 RPC 协议扩展不靠“替换底层”或“魔改框架”,而是通过实现 ProtocolInterface 并注册进 Server 配置来生效——只要协议能正确编解码、识别帧边界、处理连接生命周期,就能跑通调用链。
为什么必须实现 ProtocolInterface 而不是直接改 JsonRpcProtocol
Hyperf 的 Server 启动时会根据配置的 protocol 类名实例化对应协议对象,并在连接建立、数据到达、关闭等关键节点调用其方法。继承或复用现有协议类(如 JsonRpcProtocol)看似省事,但它的 unpack 和 pack 逻辑强耦合 JSON-RPC 2.0 结构(比如强制校验 jsonrpc 字段、id 类型),一旦你的自定义协议是二进制帧头 + TLV 或纯文本命令行格式,就会在解包阶段直接抛出 InvalidPacketException 或静默丢包。
实操建议:
- 新建类实现
ProtocolInterface,不要 extends 任何已有协议类 - 所有方法必须完整实现:
pack()、unpack()、getLength()(可选但推荐)、onConnect()、onClose() -
unpack()返回array|false:每成功解析一个完整请求/响应包就返回['data' => $payload, 'length' => $consumed];返回false表示数据不足或非法,框架会缓存并等待后续数据
unpack() 怎么写才不会粘包或错位
Hyperf 的 TCP Server 默认使用 stream 模式收包,数据是连续字节流,没有天然消息边界。如果你的协议没带长度字段或分隔符,unpack() 就无法判断一条消息到哪结束——结果就是多次调用只返回 false,最终超时断连。
常见错误现象:
- 客户端发一次请求,服务端
unpack()被反复调用却始终返回false - 两个请求粘在一起,
unpack()误把前半条当完整包解析,后半条变乱码
实操建议(以「4 字节大端长度 + 原始 payload」为例):
Hyperf 3.2.3于2026年7月30日发布,是3.2分支的官方维护版本,新增支持函数,并修复模型注释、缓存组件文档、数据库模型构建器注释和关联预加载字段等问题。
public function unpack(string $data): array|false
{
if (strlen($data) substr($data, 4, $len),
'length' => $total,
];
}
注意:getLength() 方法可返回固定长度(如 8192)或动态值,但若协议本身无长度字段,必须靠 unpack() 自己维护缓冲状态(例如用 static $buffer = '' 拼接未完成数据),否则无法应对跨 TCP 包的拆包场景。
如何让 RPC 客户端和服务端都用上你的协议
仅实现协议接口还不够——Hyperf 的 RPC 组件分两层:底层通信(Server / Client)和上层调用(ServiceClient)。协议只管字节收发,业务数据结构仍由 SerializerInterface 处理(默认 JsonSerializer)。所以你得配两处:
- 在
config/autoload/server.php中为 RPC Server 指定协议类:'protocol' => YourCustomProtocol::class - 在
config/autoload/rpc_client.php中为客户端指定相同协议:'protocol' => YourCustomProtocol::class - 如果协议需要非 JSON 序列化(比如 Protobuf),还需单独配置
serializer,且确保客户端和服务端一致
容易踩的坑:
- 服务端配了新协议,客户端仍用默认
JsonRpcProtocol→ 连接能建,但发过去的数据服务端解不出,unpack()一直返回false - 协议类没加
#[Swoole\Coroutine\Hook(flags: HookFlags::ALL)](Hyperf 3.1+ 要求),导致在协程中调用fread等阻塞函数时卡死
调试时最该盯住的三个地方
协议问题往往表现为“连接正常但无响应”,而不是报错。别急着翻源码,先看这三处:
-
unpack()是否被调用?在方法开头加var_dump(strlen($data));,确认数据是否真的到达 - 返回的
length是否准确?如果返回5但实际消耗了 10 字节,下一次传入的数据就会错位 -
pack()输出的二进制是否符合预期?用bin2hex()打印返回值,对照协议文档检查帧头、长度、校验位
二进制协议里一个字节的偏差,会导致整个链路静默失败。比起逻辑,优先验证字节层面的精确性。










