ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
#### 实现 > 应用程序创建一个swoole_client,与swoole_server三次握手建立连接,从而进行通信,通信过程中可以使用多种协议进行加解密 #### 使用 ```php // 同步请求 ZCSwoole::$app->rpcClient->request(router, params); // 异步请求 ZCSwoole::$app->rpcClient->asyncRequest(router, params); ``` - router:控制器所在路由,与http一样,都是由Router类解析 - params:请求参数,参考call_user_func_params() - 举个例子,ZCSwoole::$app->rpcClient->request('/rpc/test/saveName, array($name, $type))相当于调用下面代码: ```php $controller = new app/controllers/rpc/testController(); $controller->saveName($name, $type); ``` #### 通信协议 - rpc的通信协议由zcswoole\rpc\RpcProtocol类负责,目前有三种方式,分别为json,serialize,swoole_seralize,对应参数如下: ```php const PHP_JSON = 1; // json类型 const PHP_SERIALIZE = 2; // serialize类型 const SWOOLE_SERIALIZE = 3; // swoole_serialize类型 ``` - 整个数据包包括: 包头(pkg_len+version+command_id+encode_type) + 包体(数据根据encode_type编码,例如json_encode) - 通信协议的包头固定为16个字节,格式如下:NpkgLen/Nversion/NcommandID/NencodeType | 字段 | 名称 | | :--- | :--- | | pkgLen | 包体长度,用于检验包的完整性 | | version | 版本号,暂无用到 | | commandID | 命令ID,暂无用到 | | encodeType | 编码方式,用于解码数据 | - 编码解码使用unpack,pack函数处理,以json为例: ```php // 编码 $header = pack('N4', strlen($body), $versionID, $commandID, self::PHP_JSON); // 解码 $header = unpack('NpkgLen/Nversion/NcommandID/NencodeType', $header); ``` #### 代码 > 目前http服务已经内置rpc功能,开发者不需要单独启动rpc服务,参考文件vendor/wuzhc/zcswoole/src/command/HttpServerCommand.php ```php /** * 钩子函数,用于服务启动前设置 */ protected function beforeStart() { // 增加一个rpc服务,监听端口号9504 $rpc = $this->server->addListener('127.0.0.1', 9504, SWOOLE_SOCK_TCP); $rpc->set([]); // 需要调用 set 方法覆盖主服务器的设置 $rpc->on('receive', [$this, 'rpcReceive']); } /** * rpc接受客户端事件 * @param $server * @param int $fd * @param int $reactorID * @param string $data */ public function rpcReceive(Server $server, $fd, $reactorID, $data) { $res = null; $t1 = microtime(true); list($code, $header, $body) = RpcProtocol::decode($data); // 解包成功后处理业务 if ($code === RpcProtocol::ERR_UNPACK_OK) { $target = $body['router'] ?? ''; $params = $body['params'] ?? []; $router = new Router($target); list($controller, $action) = $router->parse(); if (class_exists($controller)) { try { $res = call_user_func_array([$controller, $action], $params); if (false === $res) { $status = Constant::STATUS_FAILED; $msg = "call $router failed"; } else { $status = Constant::STATUS_SUCCESS; $msg = 'success'; } } catch (\Exception $e) { $msg = $e->getMessage(); $status = Constant::STATUS_FAILED; } } else { $msg = "Class $controller is not exist"; $status = Constant::STATUS_FAILED; } } else { $msg = RpcProtocol::codeMsg($code); $status = Constant::STATUS_FAILED; } // 通知结果给客户端 $server->send($fd, RpcProtocol::encode([ 'data' => $res, 'time' => microtime(true) - $t1, 'status' => $status, 'msg' => $msg ], $header['encodeType'])); } ```