协议句柄是一种众所周知的对象,用于提供可使用组件命名空间发现的 FIDL 协议实现。组件框架有助于在组件之间发现协议。 功能路由描述了哪个组件应充当 。找出合适的组件后 组件管理器 使用在每个组件中找到的句柄在组件之间 该组件的命名空间。
请参考以下 fuchsia.example.Foo
协议示例:
该图展示了执行连接所涉及的主要元素:
- 提供程序组件以静态方式声明
capabilities
部分。这样,组件框架就可以 执行功能路由。 - 客户端组件在
use
部分中以静态方式请求协议 。如果 capability 路由成功,这会在客户端的命名空间中创建/svc/fuchsia.example.Foo
协议条目。 - 提供程序代码会在运行时发布实现。这会在提供商的传出目录的
/svc/fuchsia.example.Foo
中创建一个协议条目。 - 客户端代码会在运行时连接到协议句柄。这会打开与提供程序组件中运行的实现的 FIDL 连接。
发布协议实现
实现 FIDL 协议的组件会声明和公开 作为一项功能添加到其组件清单中。这样,组件框架便可将 capability 从此组件路由到请求该 capability 的拓扑中的其他组件。
{
// ...
capabilities: [
{ protocol: "fuchsia.example.Foo" },
],
expose: [
{
protocol: "fuchsia.example.Foo",
from: "self",
},
],
}
Capability Routing 会描述协议的访问权限,但不会建立连接所需的端点。组件必须使用 fuchsia.io 协议在传出目录中将实现发布为 /svc/
句柄。通过
生成的 FIDL 绑定会封装此句柄,并使提供程序能够连接
请求句柄,以开始接收 FIDL 消息。
// Serve the protocol
FooImplementation instance;
fidl::Binding<fuchsia::example::Foo> binding(&instance);
instance.event_sender_ = &binding.events();
fidl::InterfaceRequestHandler<fuchsia::example::Foo> handler =
[&](fidl::InterfaceRequest<fuchsia::example::Foo> request) {
binding.Bind(std::move(request));
};
context->outgoing()->AddPublicService(std::move(handler));
连接到协议实现
客户端组件将协议声明为
组件清单。这样,组件框架就可以确定
该组件有权访问协议实施。如果为有效路线
存在,则该组件的命名空间包含相应的 /svc/
句柄。
{
// ...
use: [
{ protocol: "fuchsia.example.Foo" },
],
}
客户端组件使用 fuchsia.io 协议与协议实现建立连接并打开通道。生成的 FIDL 绑定会封装此通道,并让客户端能够开始向提供方发送消息。
// Connect to FIDL protocol
fuchsia::example::FooSyncPtr proxy;
auto context = sys::ComponentContext::Create();
context->svc()->Connect(proxy.NewRequest());
练习:回声服务器和客户端
在本部分中,您将使用生成的 FIDL 绑定,
examples.routing.echo
,用于实现客户端和服务器组件。
启动模拟器
如果您还没有运行的实例,请启动支持网络的 FEMU:
ffx emu start workstation_eng.x64 --headless
创建服务器组件
首先,创建一个新的组件项目来实现 echo 服务器。此组件将提供 Echo
协议并处理传入请求。
在 Bazel 工作区中为名为 echo_server
的新组件创建一个新的项目目录:
mkdir -p fuchsia-codelab/echo-server
完成本部分后,项目应具有以下目录结构:
//fuchsia-codelab/echo-server
|- BUILD.bazel
|- meta
| |- echo_server.cml
|
|- main.cc
创建 echo-server/meta/echo_server.cml
组件清单,将 Echo
协议声明为服务器组件提供的功能,并将其公开以供父级王国使用:
echo-server/meta/echo_server.cml
:
{
include: [
"syslog/client.shard.cml",
"inspect/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/echo_server_cpp",
},
// Capabilities provided by this component.
capabilities: [
{ protocol: "examples.routing.echo.Echo" },
],
expose: [
{
protocol: "examples.routing.echo.Echo",
from: "self",
},
],
}
添加以下 BUILD.bazel
规则以构建和打包服务器组件:
echo-server/BUILD.bazel
:
load(
"@fuchsia_sdk//fuchsia:defs.bzl",
"fuchsia_cc_binary",
"fuchsia_component",
"fuchsia_component_manifest",
)
fuchsia_cc_binary(
name = "echo_server_cpp",
srcs = [
"main.cc",
],
deps = [
"//fuchsia-codelab/echo-fidl:examples.routing.echo.fidl_cc",
"@fuchsia_sdk//pkg/async-default",
"@fuchsia_sdk//pkg/async-loop",
"@fuchsia_sdk//pkg/async-loop-cpp",
"@fuchsia_sdk//pkg/async-loop-default",
"@fuchsia_sdk//pkg/fdio",
"@fuchsia_sdk//pkg/inspect",
"@fuchsia_sdk//pkg/inspect_component_cpp",
"@fuchsia_sdk//pkg/component_outgoing_cpp",
"@fuchsia_sdk//pkg/syslog",
],
)
fuchsia_component_manifest(
name = "manifest",
src = "meta/echo_server.cml",
component_name = "echo_server_component",
includes = [
"@fuchsia_sdk//pkg/inspect:client",
"@fuchsia_sdk//pkg/syslog:client",
],
)
fuchsia_component(
name = "echo_server_component",
component_name = "echo_server_component",
manifest = ":manifest",
visibility = ["//visibility:public"],
deps = [
":echo_server_cpp",
],
)
实现服务器
打开主源文件,并将 import 语句替换为 以下代码:
echo-server/main.cc
:
#include <fidl/examples.routing.echo/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/inspect/component/cpp/component.h>
#include <lib/syslog/global.h>
添加以下代码以实现协议请求处理脚本:
echo-server/main.cc
:
// Handler for incoming FIDL protocol requests
class EchoImplementation : public fidl::Server<examples_routing_echo::Echo> {
public:
// The handler for `examples.routing.echo/Echo.EchoString` requests.
//
// Replies back to the caller with the original request value.
void EchoString(EchoStringRequest &request,
EchoStringCompleter::Sync &completer) override {
completer.Reply({{request.value()}});
}
// Called when the FIDL connection is torn down.
void OnUnbound(fidl::UnbindInfo info,
fidl::ServerEnd<examples_routing_echo::Echo> server_end) {
if (info.is_user_initiated()) {
return;
}
if (info.is_peer_closed()) {
// The peer (the client) closed their endpoint.
FX_LOG(DEBUG, "echo_server", "Client disconnected.");
} else {
// Treat other unbind causes as errors.
FX_LOGF(ERROR, "echo_server", "Server error: %s", info.status_string());
}
}
};
每个 Echo
协议方法都有一个对应的替换函数 (EchoString()
),并包含用于发回返回值的回调接口。
此实现只是将请求中的相同字符串值“回传”到响应载荷中。
将以下代码添加到 main()
以提供 Echo
协议:
echo-server/main.cc
:
int main(int argc, const char **argv) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
component::OutgoingDirectory outgoing =
component::OutgoingDirectory(loop.dispatcher());
// Initialize inspect
inspect::ComponentInspector inspector(loop.dispatcher(), {});
inspector.Health().StartingUp();
// Serve the Echo protocol
std::unique_ptr<EchoImplementation> echo_instance =
std::make_unique<EchoImplementation>();
zx::result result = outgoing.AddProtocol<examples_routing_echo::Echo>(
std::move(echo_instance));
if (result.is_error()) {
FX_LOGF(ERROR, "echo_server", "Failed to add Echo protocol: %s",
result.status_string());
return -1;
}
result = outgoing.ServeFromStartupInfo();
if (result.is_error()) {
FX_LOGF(ERROR, "echo_server", "Failed to serve outgoing directory: %s",
result.status_string());
return -1;
}
// Component is serving and ready to handle incoming requests
inspector.Health().Ok();
return loop.Run();
}
此代码执行以下步骤以提供 Echo
协议:
- 初始化
ComponentContext
,并在传出目录的/svc/examples.routing.echo.Echo
下添加条目。 - 提供目录并开始监听传入连接。
- 将
EchoImplementation
实例作为任何 匹配Echo
个请求。
创建客户端组件
再创建一个新的组件项目来实现 echo 客户端。此组件将连接到协议实现并发送请求。
在 Bazel 工作区中为新组件创建一个新的项目目录
名为 echo_client
:
mkdir -p fuchsia-codelab/echo-client
完成本部分后,项目应具有以下目录结构:
//fuchsia-codelab/echo-client
|- BUILD.bazel
|- meta
| |- echo_client.cml
|
|- main.cc
创建 echo-client/meta/echo_client.cml
组件清单并配置
用于请求 examples.routing.echo.Echo
功能的客户端组件
由服务器公开:
echo-client/meta/echo_client.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/echo_client_cpp",
// Program arguments
args: [ "Hello Fuchsia!" ],
},
// Capabilities used by this component.
use: [
{ protocol: "examples.routing.echo.Echo" },
],
}
添加以下 BUILD.bazel
规则以构建和打包客户端组件:
echo-client/BUILD.bazel
:
load(
"@fuchsia_sdk//fuchsia:defs.bzl",
"fuchsia_cc_binary",
"fuchsia_component",
"fuchsia_component_manifest",
)
fuchsia_cc_binary(
name = "echo_client_cpp",
srcs = [
"main.cc",
],
deps = [
"//fuchsia-codelab/echo-fidl:examples.routing.echo.fidl_cc",
"@fuchsia_sdk//pkg/async-default",
"@fuchsia_sdk//pkg/async-loop",
"@fuchsia_sdk//pkg/async-loop-cpp",
"@fuchsia_sdk//pkg/async-loop-default",
"@fuchsia_sdk//pkg/fdio",
"@fuchsia_sdk//pkg/component_incoming_cpp",
"@fuchsia_sdk//pkg/syslog",
],
)
fuchsia_component_manifest(
name = "manifest",
src = "meta/echo_client.cml",
component_name = "echo_client_component",
includes = ["@fuchsia_sdk//pkg/syslog:client"],
)
fuchsia_component(
name = "echo_client_component",
component_name = "echo_client_component",
deps = [
":echo_client_cpp",
],
manifest = ":manifest",
visibility = ["//visibility:public"],
)
实现客户端
与 echo
类似,客户端将程序参数作为消息传递
发送到服务器。回想一下,参数在 program
代码块中的
echo_client.cml
:
echo-client/meta/echo_client.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/echo_client_cpp",
// Program arguments
args: [ "Hello Fuchsia!" ],
},
打开主源文件,并将导入语句替换为以下代码:
echo-client/main.cc
:
#include <fidl/examples.routing.echo/cpp/fidl.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/syslog/global.h>
#include <cstdlib>
#include <iostream>
#include <string>
将以下代码添加到 main()
以连接到 Echo
协议并发送请求:
echo-client/main.cc
:
int main(int argc, const char *argv[], char *envp[]) {
// Connect to FIDL protocol
zx::result client_end = component::Connect<examples_routing_echo::Echo>();
if (!client_end.is_ok()) {
FX_LOGF(ERROR, "echo_client", "Error connecting to Echo protocol: %s",
client_end.status_string());
return -1;
}
fidl::SyncClient client{std::move(*client_end)};
// Send messages over FIDL interface for each argument
for (int i = 1; i < argc; i++) {
fidl::Result result = client->EchoString({argv[i]});
ZX_ASSERT(result.is_ok());
auto response = result->response();
if (!response.has_value()) {
FX_LOG(INFO, "echo_client", "echo_string got empty result");
} else {
FX_LOGF(INFO, "echo_client", "Server response: %s", response->c_str());
}
}
return 0;
}
EchoSyncPtr
提供了一个封装容器,用于按名称连接到公开的 capability,并返回对开放代理接口的句柄。此代理包含
EchoString()
FIDL 协议方法。
集成组件
服务器提供的功能必须通过 组件框架为此,您需要实现一个 Realm 组件 作为父项并管理 capability 路由。
为 Realm 组件定义创建一个新的项目目录:
mkdir -p fuchsia-codelab/echo-realm
完成本部分后,项目应具有以下目录结构:
//fuchsia-codelab/echo-realm
|- BUILD.bazel
|- meta
|- echo_realm.cml
创建一个包含以下内容的新组件清单文件 echo-realm/meta/echo_realm.cml
:
以下内容:
echo-realm/meta/echo_realm.cml
:
{
// Two children: a server and client.
children: [
{
name: "echo_server",
url: "#meta/echo_server.cm",
},
{
name: "echo_client",
url: "#meta/echo_client.cm",
},
],
// Route Echo service from server to client.
offer: [
{
protocol: "examples.routing.echo.Echo",
from: "#echo_server",
to: "#echo_client",
},
{
protocol: "fuchsia.logger.LogSink",
from: "parent",
to: [
"#echo_client",
"#echo_server",
],
},
],
}
这将创建一个组件领域,其中服务器和客户端作为子组件,
并将 examples.routing.echo.Echo
协议功能路由到
客户端。
添加一个 BUILD.bazel
文件,为 Realm 组件创建一个 build 目标,
包含服务器和客户端的 Fuchsia 软件包:
echo-realm/BUILD.bazel
:
load(
"@fuchsia_sdk//fuchsia:defs.bzl",
"fuchsia_component",
"fuchsia_component_manifest",
"fuchsia_package",
)
fuchsia_component_manifest(
name = "manifest",
src = "meta/echo_realm.cml",
component_name = "echo_realm",
)
fuchsia_component(
name = "echo_realm",
component_name = "echo_realm",
manifest = ":manifest",
)
fuchsia_package(
name = "pkg",
package_name = "echo-realm",
visibility = ["//visibility:public"],
components = [
":echo_realm",
"//fuchsia-codelab/echo-client:echo_client_component",
"//fuchsia-codelab/echo-server:echo_server_component",
],
)
构建并将软件包发布到 fuchsiasamples.com
代码库:
bazel run //fuchsia-codelab/echo-realm:pkg.publish -- \
--repo_name fuchsiasamples.com
将组件添加到拓扑中
您需要将您的组件添加到受限集合 ffx-laboratory
中
用于在产品的核心领域进行开发。借助集合
在运行时动态创建和销毁的组件。
通过将 echo-realm
组件网址和 ffx-laboratory
中的适当标识符传递给 ffx component create
来创建组件实例:
ffx component create /core/ffx-laboratory:echo-realm \
fuchsia-pkg://fuchsiasamples.com/echo-realm#meta/echo_realm.cm
然后,使用 ffx component resolve
解析 echo-realm
组件:
ffx component resolve /core/ffx-laboratory:echo-realm
验证服务器和客户端的实例是否也使用 ffx component show
创建为子组件:
ffx component show echo
Moniker: /core/ffx-laboratory:echo-realm/echo_client
URL: #meta/echo_client.cm
Type: CML static component
Component State: Unresolved
Execution State: Stopped
Moniker: /core/ffx-laboratory:echo-realm/echo_server
URL: #meta/echo_server.cm
Type: CML static component
Component State: Unresolved
Execution State: Stopped
Moniker: /core/ffx-laboratory:echo-realm
URL: fuchsia-pkg://fuchsiasamples.com/echo-realm#meta/echo_realm.cm
Type: CML dynamic component
Component State: Resolved
Execution State: Stopped
Merkle root: 666c40477785f89b0ace22b30d65f1338f1d308ecceacb0f65f5140baa889e1b
验证组件互动
使用 ffx component start
启动现有客户端组件实例:
ffx component start /core/ffx-laboratory:echo-realm/echo_client
打开另一个终端窗口,验证客户端组件的日志输出:
ffx log --filter echo
您应该会在设备日志中看到以下输出:
[echo_client][I] Server response: Hello, Fuchsia!
在客户端连接到
examples.routing.echo.Echo
功能并继续运行
其他 FIDL 请求。
使用 ffx component show
查看在组件中运行的 echo 服务器
实例树:
ffx component show echo_server
Moniker: /core/ffx-laboratory:echo-realm/echo_server
URL: #meta/echo_server.cm
Type: CML static component
Component State: Resolved
Incoming Capabilities: fuchsia.logger.LogSink
Exposed Capabilities: diagnostics
examples.routing.echo.Echo
Execution State: Running
Job ID: 474691
Process ID: 474712
Running for: 2026280474361 ticks
Merkle root: 666c40477785f89b0ace22b30d65f1338f1d308ecceacb0f65f5140baa889e1b
Outgoing Capabilities: diagnostics
examples.routing.echo.Echo
销毁实例
使用以下命令清理 echo-realm
实例:
ffx component destroy /core/ffx-laboratory:echo-realm