# 封装器 ```php interface IPack { function encode($buffer); function decode($buffer); function pack($data, $topic = null); function unPack($data); function getProbufSet(); function errorHandle($e, $fd); } ``` 需要实现6个方法 encode和decode分别是协议头的封装和解析工作,pack和unPack是协议体的序列化和反序列化工作,getProbufSet将返回swoole的set中关于协议头解析相关的配置,errorHandle是协议解析出错后的处理函数。 默认提供了几组简易配置 ```php class LenJsonPack implements IPack { protected $package_length_type = 'N'; protected $package_length_type_length = 4; protected $package_length_offset = 0; protected $package_body_offset = 0; protected $last_data = null; protected $last_data_result = null; /** * 数据包编码 * @param $buffer * @return string * @throws SwooleException */ public function encode($buffer) { $total_length = $this->package_length_type_length + strlen($buffer) - $this->package_body_offset; return pack($this->package_length_type, $total_length) . $buffer; } /** * @param $buffer * @return string */ public function decode($buffer) { return substr($buffer, $this->package_length_type_length); } public function pack($data, $topic = null) { if ($this->last_data != null && $this->last_data == $data) { return $this->last_data_result; } $this->last_data = $data; $this->last_data_result = $this->encode(json_encode($data, JSON_UNESCAPED_UNICODE)); return $this->last_data_result; } public function unPack($data) { $value = json_decode($this->decode($data)); if (empty($value)) { throw new SwooleException('json unPack 失败'); } return $value; } public function getProbufSet() { return [ 'open_length_check' => true, 'package_length_type' => $this->package_length_type, 'package_length_offset' => $this->package_length_offset, //第N个字节是包长度的值 'package_body_offset' => $this->package_body_offset, //第几个字节开始计算长度 'package_max_length' => 2000000, //协议最大长度) ]; } public function errorHandle($e, $fd) { get_instance()->close($fd); } } ``` ## 自定义协议举例 协议 = 包头(4字节 `msg_type` + 4字节包体长) + 包体(protobuf) ```php <?php namespace app\Pack; use Server\Pack\IPack; use Server\CoreBase\SwooleException; use Game\Protobuf\Ping; use Game\Protobuf\Pong; use stdClass; class GamePack implements IPack { /** * 数据包编码 * @param $buffer * @return string * @throws SwooleException */ public function encode($buffer) { return substr($buffer, 0, 4) . pack('N', strlen($buffer) - 4) . substr($buffer, 4); } /** * @param $buffer * @return string */ public function decode($buffer) { return substr($buffer, 0, 4) . substr($buffer, 8); } // 包长还是会传递过来 public function unPack($data) { $msgType = unpack('N', substr($data, 0, 4))[1]; $body = substr($data, 8); $dataMsg = new stdClass(); $dataMsg->msg_type = $msgType; // protobuf Exception // try { // $to->mergeFromString($data); // } catch (Exception $e) { // // Handle parsing error from invalid data. // ... // } if ($msgType == 1) { $msg = new Ping(); $msg->mergeFromString($body); $dataMsg->uid = $msg->getUid(); } else if ($msgType == 2) { $msg = new Pong(); $dataMsg->uid = $msg->getUid(); } return $dataMsg; } public function Pack($data, $topic = null) { $msgType = $data->msg_type; if ($msgType == 1) { $msg = new Ping(); $msg->setUid($data->uid); } else if ($msgType == 2) { $msg = new Pong(); $msg->setUid($data->uid); } $data = $msg->serializeToString(); return pack('N', $msgType) . pack('N', strlen($data)). $data; } public function getProbufSet() { return [ 'open_length_check' => true, 'package_length_type' => 'N', 'package_length_offset' => 4, //第N个字节是包长度的值 'package_body_offset' => 8, //第几个字节开始计算长度 'package_max_length' => 2000000, //协议最大长度 ]; } public function errorHandle($e, $fd) { get_instance()->close($fd); } } ``` protobuf 定义如下: ``` syntax = "proto3"; package game.protobuf; enum MsgType { PING = 1; PONG = 2; }; message Ping { uint64 uid = 1; }; message Pong { uint64 uid = 1; }; ```