前提条件
本教程基于编译 FIDL 教程。对于 完整的 FIDL 教程,请参阅概览。
概览
本教程介绍了如何实现 FIDL 协议
(fuchsia.examples.Echo
) 并在 Fuchsia 上运行它。此协议有一种方法
三种类型:触发后忘记方法、双向方法和事件:
@discoverable
closed protocol Echo {
strict EchoString(struct {
value string:MAX_STRING_LENGTH;
}) -> (struct {
response string:MAX_STRING_LENGTH;
});
strict SendString(struct {
value string:MAX_STRING_LENGTH;
});
strict -> OnString(struct {
response string:MAX_STRING_LENGTH;
});
};
本文档介绍了如何完成以下任务:
- 实现 FIDL 协议。
- 在 Fuchsia 上构建并运行软件包。
- 提供 FIDL 协议。
本教程首先创建一个投放到 Fuchsia 设备的组件 并运行然后,逐步添加功能,让服务器正常运行。
如果要自行编写代码,请删除以下目录:
rm -r examples/fidl/hlcpp/server/*
创建组件
要创建组件,请执行以下操作:
将
main()
函数添加到examples/fidl/hlcpp/server/main.cc
:#include <stdio.h> int main(int argc, const char** argv) { printf("Hello, world!\n"); return 0; }
在
examples/fidl/hlcpp/server/BUILD.gn
中声明服务器的目标:import("//build/components.gni") # Declare an executable for the server. This produces a binary with the # specified output name that can run on Fuchsia. executable("bin") { output_name = "fidl_echo_hlcpp_server" sources = [ "main.cc" ] } # Declare a component for the server, which consists of the manifest and the # binary that the component will run. fuchsia_component("echo-server") { component_name = "echo_server" manifest = "meta/server.cml" deps = [ ":bin" ] } # Declare a package that contains a single component, our server. fuchsia_package("echo-hlcpp-server") { package_name = "echo-hlcpp-server" deps = [ ":echo-server" ] }
要启动并运行服务器组件,需要设置 定义:
- 服务器构建为在 Fuchsia 上运行的原始可执行文件。
- 一种组件,设置为仅运行服务器可执行文件, 并使用该组件的清单文件加以描述
- 然后,将组件放入软件包中,包是软件的 在 Fuchsia 上的应用分发。在本例中, 包只包含 单个组件。
如需详细了解软件包、组件以及如何构建它们,请参阅 构建组件页面。
在
examples/fidl/hlcpp/server/meta/server.cml
中添加组件清单:{ include: [ "syslog/client.shard.cml" ], // Information about the program to run. program: { // Use the built-in ELF runner. runner: "elf", // The binary to run for this component. binary: "bin/fidl_echo_hlcpp_server", }, // Capabilities provided by this component. capabilities: [ { protocol: "fuchsia.examples.Echo" }, ], expose: [ { protocol: "fuchsia.examples.Echo", from: "self", }, ], }
将该服务器添加到您的构建配置中:
fx set core.x64 --with //examples/fidl/hlcpp/server:echo-hlcpp-server
构建 Fuchsia 映像:
fx build
实现服务器
添加对 FIDL 库的依赖项
将
fuchsia.examples
FIDL 库目标添加为examples/fidl/hlcpp/server/BUILD.gn
中的executable
:executable("bin") { output_name = "fidl_echo_hlcpp_server" sources = [ "main.cc" ] deps = [ "//examples/fidl/fuchsia.examples" ] }
在
examples/fidl/hlcpp/server/main.cc
顶部导入 HLCPP 绑定:#include <fuchsia/examples/cpp/fidl.h>
添加协议的实现
将以下代码添加到 main.cc
中的 main()
函数上方:
class EchoImpl : public fuchsia::examples::Echo {
public:
void EchoString(std::string value, EchoStringCallback callback) override { callback(value); }
void SendString(std::string value) override {
if (event_sender_) {
event_sender_->OnString(value);
}
}
fuchsia::examples::Echo_EventSender* event_sender_;
};
该实现包含以下元素:
- 该类是生成的协议类和 覆盖与协议方法对应的纯虚拟方法。
EchoString
的方法通过调用 回调。SendString
的方法不接受回调,因为此方法会接受回调 没有响应。相反,实现会发送OnString
事件 使用Echo_EventSender
。- 该类包含一个指向
Echo_EventSender
的指针。此设置将设置为main()
函数后面的部分。
您可以通过运行以下命令来验证实现是否正确:
fx build
提供协议
要运行实现 FIDL 协议的组件,请向 组件管理器,以便将该 FIDL 协议提供给其他 组件。然后,组件管理器会路由对 echo 协议的所有请求 发送到我们的服务器。
要执行这些请求,组件管理器需要协议的名称 以及在有传入请求时应调用的处理程序。 连接到与指定名称匹配的协议。
传递给它的处理程序是一个函数,它将获取一个通道(其远程
end 归客户端所有),并将其绑定到一个 fidl::Binding
使用服务器实现进行初始化。fidl::Binding
是一个类
从采用 FIDL 协议实现和通道的 FIDL 运行时中获取,
然后监听通道是否传入请求。然后解码
请求,将它们分派给服务器类中的正确方法
将任何响应写回客户端。我们的主要方法将继续监听
异步循环传入请求的影响。
此完整过程将在 开放协议的生命周期。
添加新的依赖项
此新代码需要以下额外的依赖项:
"//sdk/lib/async-loop:async-loop-cpp"
和"//sdk/lib/async-loop:async-loop-default"
:这些库包含 异步循环代码"//sdk/lib/sys/cpp"
:组件框架 C++ 运行时,其中包含 用于与组件环境交互的实用程序代码。
将库目标添加为
executable
的依赖项examples/fidl/hlcpp/server/BUILD.gn
:executable("bin") { output_name = "fidl_echo_hlcpp_server" sources = [ "main.cc" ] deps = [ "//examples/fidl/fuchsia.examples:fuchsia.examples_hlcpp", "//sdk/lib/sys/cpp", "//zircon/system/ulib/async-loop:async-loop-cpp", "//zircon/system/ulib/async-loop:async-loop-default", ] }
在
examples/fidl/hlcpp/server/main.cc
的顶部导入这些依赖项:#include <lib/async-loop/cpp/loop.h> #include <lib/async-loop/default.h> #include <lib/fidl/cpp/binding.h> #include <lib/sys/cpp/component_context.h> #include <lib/sys/cpp/service_directory.h>
初始化事件循环
第一个方面是使用异步循环:
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();
}
代码首先初始化循环并将其注册为默认调度程序
用于当前线程首先,因为 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();
}
要运行绑定,需要具备以下两项:
- 协议的实现。
- 绑定将在其中监听该协议的消息的通道。 该绑定首先使用 echo 实现进行初始化, 之后会绑定到某个频道
该代码还会设置用于向客户端发送事件的事件发送者。
对 Binding
使用 events()
方法获取事件发送者,然后将其传递给
EchoImpl
类。
定义协议请求处理程序
接下来,该代码为来自客户端的传入请求定义了一个处理程序:
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();
}
- “传入请求”不是对
Echo
特定方法的请求 而是从客户端发出的连接到Echo
协议的实现。 - 请求定义为
fidl::InterfaceRequest<Echo>
。这是一个 一个针对通道的类型安全的封装容器,用于指示以下两种情况:InterfaceRequest
表示这是通道的服务器端(即 客户端连接到通道的远程端)- 模板参数
Echo
表示客户端期望服务器Echo
协议会将其自身绑定到此通道。客户 与之类似的是 表示此通道另一端)是fidl::InterfaceHandle<Echo>
。
- 处理程序直接获取从客户端发送的通道,并将其绑定到
Echo
绑定。 - 发生这种情况后,
Binding
会开始处理通道上的消息 符合Echo
协议。这是一个协议请求示例 流水线,这在后续教程中有所介绍。
注册协议请求处理程序
最后,代码向组件管理器注册处理程序:
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();
}
第一行会初始化并提供传出目录,其中包含 第二行 将处理程序添加到传出目录。
处理程序外,隐式第二个参数是
资源。默认情况下,此参数是协议的名称
这是由于存在 [Discoverable]
Echo
协议中的属性。换句话说,执行
使用此行,您应该能够在组件的 /out
目录上调用 ls
您会看到一个名为 fuchsia.examples.Echo
的条目。
测试服务器
重建:
fx build
然后运行服务器组件:
ffx component run /core/ffx-laboratory:echo_server fuchsia-pkg://fuchsia.com/echo-hlcpp-server#meta/echo_server.cm
注意:组件是使用其 组件网址 , 该值取决于 `fuchsia-pkg://` 架构。
您应该会在设备日志中看到类似于以下内容的输出
(ffx log
):
[ffx-laboratory:echo_server][][I] Running echo server
服务器现在正在运行并等待传入请求。
下一步是编写一个发送 Echo
协议请求的客户端。
目前,您可以直接终止服务器组件:
ffx component destroy /core/ffx-laboratory:echo_server