通訊協定控制代碼為知名物件,提供實作 FIDL 通訊協定,可透過元件命名空間探索。 功能轉送描述應做為任何特定用戶端的供應器。
請參考下列 fuchsia.example.Foo
通訊協定的範例:
下圖醒目顯示執行連線的主要元素:
- 供應商元件會以靜態方式宣告資訊清單
capabilities
區段中的通訊協定。這讓元件架構可以執行能力轉送。 - 用戶端元件會以靜態方式要求資訊清單
use
區段中的通訊協定。如果能力轉送成功,這會在用戶端的命名空間中建立/svc/fuchsia.example.Foo
通訊協定項目。 - 供應商程式碼會在執行階段發布實作項目。這會在供應器的傳出目錄的
/svc/fuchsia.example.Foo
建立通訊協定項目。 - 用戶端程式碼會在執行階段與通訊協定控制代碼連線。這個動作會開啟 FIDL 連線,連結至在供應器元件中執行的實作。
發布通訊協定實作
實作 FIDL 通訊協定的元件會在其元件資訊清單中宣告並公開該通訊協定為能力。這可讓元件架構執行能力轉送,從這個元件到要求該能力的拓撲中的其他項目。
{
// ...
capabilities: [
{ protocol: "fuchsia.example.Foo" },
],
expose: [
{
protocol: "fuchsia.example.Foo",
from: "self",
},
],
}
功能轉送會說明通訊協定的存取權限,但不會建立連線所需的端點。元件必須使用 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());
練習:Echo 伺服器和用戶端
在本節中,您將使用針對 examples.routing.echo
產生的 FIDL 繫結實作用戶端和伺服器元件。
啟動模擬器
如果您尚未擁有執行中的執行個體,請透過網路支援啟動 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(
"@rules_fuchsia//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",
],
)
實作伺服器
開啟主要來源檔案,並將匯入陳述式替換為下列程式碼:
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/inspect/component/cpp/component.h>
#include <lib/component/outgoing/cpp/outgoing_directory.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()
),並提供可傳回傳回值的回呼介面。
這種實作方式會直接「echo」在回應酬載中從要求傳回相同的字串值。
將下列程式碼新增至 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(outgoing, 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
底下新增項目。 - 提供目錄,然後開始監聽連入連線。
- 針對任何相符的
Echo
要求,將EchoImplementation
執行個體做為要求處理常式進行附加。
建立用戶端元件
建立另一個元件專案以實作 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(
"@rules_fuchsia//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
類似,用戶端會將程式引數做為訊息傳送至伺服器。提醒您,引數已按照 echo_client.cml
的 program
區塊說明:
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/service_client.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
提供包裝函式,可依名稱連線至已公開的能力,並將控制代碼傳回開啟 Proxy 介面。這個 Proxy 包含 EchoString()
FIDL 通訊協定方法。
整合元件
伺服器提供的功能必須透過元件架構轉送至用戶端。如要啟用這項功能,您須實作運作領域元件做為父項,並管理能力轉送。
為領域元件定義建立新的專案目錄:
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
檔案,建立領域元件的建構目標,以及包含伺服器和用戶端的 Fuchsia 套件:
echo-realm/BUILD.bazel
:
load(
"@rules_fuchsia//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