連結元件

通訊協定句柄是已知物件,可提供可透過元件命名空間發現的 FIDL 通訊協定實作項目。元件架構可協助在元件之間發現通訊協定。能力路由會說明哪個元件應充當任何指定用戶端的供應器。一旦找到適當的元件,Component Manager 就會使用每個元件命名空間中的句柄,在元件之間建立連線。

請參考以下範例的 fuchsia.example.Foo 通訊協定:

連接元件如何結合能力的圖表
包括轉送和通訊協定提供元件必須提供其他元件所需的通訊協定實作項目。

此圖表醒目顯示與執行連線相關的主要元素:

  1. 提供者元件會在資訊清單的 capabilities 部分中,靜態宣告該通訊協定。這樣一來,元件架構就能執行功能路由。
  2. 用戶端元件會以靜態方式要求 use 區段中的通訊協定 資訊清單這會建立 /svc/fuchsia.example.Foo 通訊協定項目 呼叫。
  3. 供應器程式碼會在執行階段發布實作項目。這會在提供者的傳出目錄中,在 /svc/fuchsia.example.Foo 建立通訊協定項目。
  4. 用戶端程式碼會在執行階段連結至通訊協定句柄。這會開啟與供應器元件中執行的實作項目的 FIDL 連線。

發布通訊協定實作

實作 FIDL 通訊協定的元件會宣告公開 作為元件資訊清單中的能力這會啟用元件 從這個元件到 要求這些能力的拓撲

{
    // ...
    capabilities: [
        { protocol: "fuchsia.example.Foo" },
    ],
    expose: [
        {
            protocol: "fuchsia.example.Foo",
            from: "self",
        },
    ],
}

功能轉送可用來說明通訊協定的存取權限,但確實會 未建立連線所需的端點。元件必須發布 使用/svc/ fuchsia.io 通訊協定。產生的 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 伺服器和用戶端

在本節中,您將使用產生的 FIDL 繫結: examples.routing.echo:用於實作用戶端和伺服器元件。

啟動模擬器

如果您尚未啟動執行個體,請啟動支援網路功能的 FEMU:

ffx emu start workstation_eng.x64 --headless

建立伺服器元件

請先建立新的元件專案,實作回音伺服器。這個元件會提供 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",
    ],
)

實作伺服器

開啟主要來源檔案,並將匯入陳述式替換為以下程式碼:

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()),並包含回呼介面來傳回傳回值。

這項實作會直接「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(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 通訊協定:

  1. 初始化 ComponentContext,並在傳出目錄的 /svc/examples.routing.echo.Echo 下方新增項目。
  2. 提供目錄並開始監聽傳入的連線。
  3. 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 會提供包裝函式,以透過 並傳回處理常式開啟的 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(
    "@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-laboratoryffx 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