前提条件
本教程以 HLCPP 使用入门教程为基础。
概览
在 Fuchsia 上使用 FIDL 的一个常见方面是跨协议自行传递协议。更确切地说,许多消息包含通道的客户端或服务器端,这些通道用于通过特定协议进行通信。在这种情况下,客户端端表示通道的远程端实现指定的协议,而服务器端端表示远程端正在针对指定的协议发出请求。客户端和服务器端的另一组术语是协议和协议请求。
本教程涵盖以下内容:
- 这些客户端和服务器的使用会在 FIDL 和 HLCPP FIDL 绑定中结束。
- 请求管道模式及其优势。
本教程的完整示例代码位于 //examples/fidl/hlcpp/request_pipelining
。
FIDL 协议
为此,本教程实现了 fuchsia.examples 库中的 EchoLauncher
协议:
@discoverable
closed protocol EchoLauncher {
strict GetEcho(struct {
echo_prefix string:MAX_STRING_LENGTH;
}) -> (resource struct {
response client_end:Echo;
});
strict GetEchoPipelined(resource struct {
echo_prefix string:MAX_STRING_LENGTH;
request server_end:Echo;
});
};
这是一个允许客户端检索 Echo
协议实例的协议。客户端可以指定前缀,生成的 Echo
实例会将该前缀添加到每个响应。
可以使用以下两种方法实现此目的:
GetEcho
:将前缀作为请求,并使用连接到Echo
协议实现的通道的客户端端进行响应。在响应中收到客户端端后,客户端可以开始使用该客户端端通过Echo
协议发出请求。GetEchoPipelined
:将通道的服务器端作为请求参数之一,并将Echo
的实现绑定到该通道。系统会假定发出请求的客户端已持有客户端,并将在调用GetEchoPipeliend
后开始在该频道上发出Echo
请求。
顾名思义,后者使用一种称为协议请求管道的模式,是首选方法。本教程实现了这两种方法。
实现服务器
实现 Echo 协议
Echo
的这种实现允许指定前缀,以便区分 Echo
服务器的不同实例:
class EchoImpl : public fuchsia::examples::Echo {
public:
explicit EchoImpl(std::string prefix) : prefix_(prefix) {}
void EchoString(std::string value, EchoStringCallback callback) override {
std::cout << "Got echo request for prefix " << prefix_ << std::endl;
callback(prefix_ + value);
}
void SendString(std::string value) override {}
const std::string prefix_;
};
SendString
处理程序为空,因为客户端只使用 EchoString
。
实现 EchoLauncher 协议
此类使用绑定集来跟踪其启动的所有 Echo
实例:
class EchoLauncherImpl : public fuchsia::examples::EchoLauncher {
public:
void GetEcho(std::string echo_prefix, GetEchoCallback callback) override {
std::cout << "Got non pipelined request" << std::endl;
fidl::InterfaceHandle<fuchsia::examples::Echo> client_end;
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end = client_end.NewRequest();
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
callback(std::move(client_end));
}
void GetEchoPipelined(std::string echo_prefix,
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end) override {
std::cout << "Got pipelined request" << std::endl;
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
}
fidl::BindingSet<fuchsia::examples::Echo, std::unique_ptr<fuchsia::examples::Echo>> bindings_;
};
该代码不仅明确指定了绑定集模板化的协议,还明确指定了它所存储的绑定的指针类型。该代码使用 unique_ptr
而非原始指针,以便绑定集拥有 EchoImpl
的实例。
下面是两个方法的实现:
class EchoLauncherImpl : public fuchsia::examples::EchoLauncher {
public:
void GetEcho(std::string echo_prefix, GetEchoCallback callback) override {
std::cout << "Got non pipelined request" << std::endl;
fidl::InterfaceHandle<fuchsia::examples::Echo> client_end;
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end = client_end.NewRequest();
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
callback(std::move(client_end));
}
void GetEchoPipelined(std::string echo_prefix,
fidl::InterfaceRequest<fuchsia::examples::Echo> server_end) override {
std::cout << "Got pipelined request" << std::endl;
bindings_.AddBinding(std::make_unique<EchoImpl>(echo_prefix), std::move(server_end));
}
fidl::BindingSet<fuchsia::examples::Echo, std::unique_ptr<fuchsia::examples::Echo>> bindings_;
};
对于 GetEcho
,代码首先需要实例化通道的两端。它使用服务器端创建 Binding
,然后通过客户端发回响应。对于 GetEchoPipelined
,客户端已完成创建通道两端的工作。它会保留一端,并将另一端传递给服务器,因此代码只需将其绑定到一个 Echo
实现即可。
提供 EchoLauncher 协议
主循环与服务器教程中的相同,但提供的是 EchoLauncher
,而不是 Echo
。
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
EchoLauncherImpl impl;
fidl::Binding<fuchsia::examples::EchoLauncher> binding(&impl);
fidl::InterfaceRequestHandler<fuchsia::examples::EchoLauncher> handler =
[&](fidl::InterfaceRequest<fuchsia::examples::EchoLauncher> request) {
binding.Bind(std::move(request));
};
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
context->outgoing()->AddPublicService(std::move(handler));
std::cout << "Running echo launcher server" << std::endl;
return loop.Run();
}
构建服务器
(可选)要检查代码是否正确,请尝试构建服务器:
将您的 GN build 配置为包含该服务器:
fx set core.x64 --with //examples/fidl/hlcpp/request_pipelining/server:echo-server
构建 Fuchsia 映像:
fx build
实现客户端
连接到 EchoLauncher
服务器后,客户端代码使用 GetEcho
连接到 Echo
的一个实例,使用 GetEchoPipelined
连接到另一个实例,然后对每个实例发出 EchoString
请求。
非流水线代码如下:
fuchsia::examples::EchoPtr echo;
auto callback = [&](fidl::InterfaceHandle<fuchsia::examples::Echo> client_end) {
std::cout << "Got non pipelined response" << std::endl;
echo.Bind(std::move(client_end));
echo->EchoString("hello!", [&](std::string response) {
std::cout << "Got echo response " << response << std::endl;
if (++num_responses == 2) {
loop.Quit();
}
});
};
echo_launcher->GetEcho("not pipelined: ", std::move(callback));
此代码有两层回调:
- 外层处理启动器请求。
- 内层会处理
EchoString
请求。
此外,该代码在外部作用域中实例化 EchoPtr
,然后在回调内对其执行 Bind
操作,而不是调用 fidl::InterfaceRequest<T>::Bind
。这是因为收到 echo 响应(很可能在顶级回调返回之后)时,代理需要位于范围内。
尽管必须初始化通道,但流水线代码要简单得多:
fuchsia::examples::EchoPtr echo_pipelined;
echo_launcher->GetEchoPipelined("pipelined: ", echo_pipelined.NewRequest());
echo_pipelined->EchoString("hello!", [&](std::string response) {
std::cout << "Got echo response " << response << std::endl;
if (++num_responses == 2) {
loop.Quit();
}
});
构建客户端
(可选)要检查内容是否正确,请尝试构建客户端:
配置 GN build 以包含该客户端:
fx set core.x64 --with //examples/fidl/hlcpp/request_pipelining/client:echo-client
构建 Fuchsia 映像:
fx build
运行示例代码
fuchsia.examples.Echo
fuchsia.examples.EchoLauncher
配置 build 以添加所提供的包含 echo 领域、服务器和客户端的软件包:
fx set core.x64 --with //examples/fidl/hlcpp:echo-launcher-hlcpp
构建 Fuchsia 映像:
fx build
运行
echo_realm
组件。这会创建客户端和服务器组件实例并路由功能:ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-launcher-hlcpp#meta/echo_realm.cm
启动
echo_client
实例:ffx component start /core/ffx-laboratory:echo_realm/echo_client
服务器组件在客户端尝试连接到 EchoLauncher
协议时启动。您应该会在设备日志 (ffx log
) 中看到类似于以下内容的输出:
[echo_server][][I] Running echo launcher server
[echo_server][][I] Got non pipelined request
[echo_server][][I] Got pipelined request
[echo_server][][I] Got echo request for prefix pipelined:
[echo_client][][I] Got non pipelined response
[echo_client][][I] Got echo response pipelined: hello!
[echo_server][][I] Got echo request for prefix not pipelined:
[echo_client][][I] Got echo response not pipelined: hello!
根据打印订单,您可以看到,采用流水线的充电盒速度更快。即使首先发送非流水线请求,也会先发送流水线用例的 echo 响应,因为请求流水线节省了客户端与服务器之间的往返。请求管道也会简化代码。
如需详细了解协议请求管道,包括如何处理可能失败的协议请求,请参阅 FIDL API 评分准则。
终止领域组件以停止执行并清理组件实例:
ffx component destroy /core/ffx-laboratory:echo_realm