前提条件
本教程以 HLCPP 使用入门教程为基础。
概览
本教程更新了入门教程中的 Echo 客户端,以便与服务器建立多个连接,并更新 Echo 服务器以处理多个客户端连接。如需运行服务器的多个实例(或多个 FIDL 协议),请参阅有关服务的教程。
本教程的完整示例代码位于 //examples/fidl/hlcpp/multiple_clients
。
实现服务器
在之前的实现中,main()
函数初始化了单个 fidl::Binding
,并将所有传入请求绑定到其中:
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
EchoImpl impl;
fidl::Binding<fuchsia::examples::Echo> binding(&impl);
impl.event_sender_ = &binding.events();
fidl::InterfaceRequestHandler<fuchsia::examples::Echo> handler =
[&](fidl::InterfaceRequest<fuchsia::examples::Echo> request) {
binding.Bind(std::move(request));
};
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
context->outgoing()->AddPublicService(std::move(handler));
printf("Running echo server\n");
return loop.Run();
}
这意味着,如果第二个客户端同时尝试连接到服务器,则对 binding.Bind
的第二次调用会覆盖来自第一个客户端的通道。如需支持多个客户端,请使用 fidl::BindingSet
跟踪多个 fidl::Binding
(每个客户端一个):
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
EchoImpl impl;
fidl::BindingSet<fuchsia::examples::Echo> bindings;
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
context->outgoing()->AddPublicService(bindings.GetHandler(&impl));
printf("Running echo server\n");
return loop.Run();
}
绑定集还简化了代码,因为它不再需要创建自定义处理程序。绑定集包含一个 GetHandler
方法,该方法返回一个处理程序,用于创建新的 Binding
并将其存储在矢量中。
如需使用 fidl::BindingSet
,请添加 lib/fidl/cpp/binding_set.h
。
实现客户端
为了管理连接到某个协议的多个客户端,FIDL HLCPP 运行时库为 fidl::BindingSet
提供了一个模拟:fidl::InterfacePtrSet
。使用该类编写可与同一协议建立多个连接的代码:
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto context = sys::ComponentContext::Create();
fidl::InterfacePtrSet<fuchsia::examples::Echo> echoers;
for (int i = 0; i < kNumClients; i++) {
fuchsia::examples::EchoPtr proxy;
context->svc()->Connect(proxy.NewRequest());
proxy.set_error_handler([&loop](zx_status_t status) {
std::cout << "Error reading incoming message: " << status << std::endl;
loop.Quit();
});
echoers.AddInterfacePtr(std::move(proxy));
}
size_t responses = 0;
for (auto& echoer : echoers.ptrs()) {
(*echoer)->EchoString("Hello echoer " + std::to_string(responses++), [&](std::string response) {
std::cout << "Got response " << response << std::endl;
if (responses == echoers.size()) {
loop.Quit();
}
});
}
loop.Run();
return responses == kNumClients ? 0 : 1;
}
设置代理并发出请求的代码与客户端教程中的相同,只是它使用接口指针集来简化向一组客户端广播消息的过程。使用 fidl::InterfacePtrSet
和 fidl::BindingSet
的另一个好处是,任何在其通道上遇到错误的绑定或接口指针都会自动从集合中移除。
如需使用 fidl::InterfacePtrSet
,请添加 lib/fidl/cpp/interface_ptr_set.h
。
运行示例
为了让客户端和服务器使用 Echo
协议进行通信,组件框架必须将 fuchsia.examples.Echo
功能从服务器路由到客户端。
配置 build 以添加所提供的包含 echo 领域、服务器和客户端的软件包:
fx set core.x64 --with //examples/fidl/hlcpp:echo-hlcpp-multi-client
构建 Fuchsia 映像:
fx build
运行
echo_realm
组件。这会创建客户端和服务器组件实例并路由功能:ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-hlcpp-multi-client#meta/echo_realm.cm
启动
echo_client
实例:ffx component start /core/ffx-laboratory:echo_realm/echo_client
服务器组件在客户端尝试连接到 Echo
协议时启动。您应该会在设备日志 (ffx log
) 中看到类似于以下内容的输出:
[echo_server][][I] Running echo server
[echo_client][][I] Got response Hello echoer 0
终止领域组件以停止执行并清理组件实例:
ffx component destroy /core/ffx-laboratory:echo_realm