實作 HLCPP FIDL 伺服器

必要條件

本教學課程是以編譯 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/*

建立元件

如何建立元件:

  1. main() 函式新增至 examples/fidl/hlcpp/server/main.cc

    #include <stdio.h>
    
    int main(int argc, const char** argv) {
      printf("Hello, world!\n");
      return 0;
    }
    
  2. 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" ]
    }
    

    為了讓伺服器元件正常執行,在 Pod 中 定義:

    • 專為在 Fuchsia 上執行的伺服器原始可執行檔。
    • 設定為單純執行伺服器執行檔的元件。 這些指令是透過元件的資訊清單檔案描述
    • 元件會放入套件中,也就是軟體單位 在 Fuchsia 的發行。在這個情況下,套件僅包含 單一元件

    如要進一步瞭解套件、元件和建立方式,請參閱 「建構元件」頁面。

  3. 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",
            },
        ],
    }
    
    
  4. 將伺服器新增至建構設定:

    fx set core.x64 --with //examples/fidl/hlcpp/server:echo-hlcpp-server
  5. 建構 Fuchsia 映像檔:

    fx build

實作伺服器

在 FIDL 程式庫中新增依附元件

  1. 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" ]
    }
    
  2. 匯入 examples/fidl/hlcpp/server/main.cc 頂端的 HLCPP 繫結:

    #include <fuchsia/examples/cpp/fidl.h>
    

新增通訊協定實作項目

將以下內容新增至 main() 函式的 main.cc 中:

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 通訊協定的所有要求。 我們的伺服器

如要執行這些要求,元件管理員需要通訊協定的名稱 以及當它有任何傳入要求時,應呼叫的處理常式 會連線至符合指定名稱的通訊協定。

傳遞至呼叫端的處理常式是一個函式,可接收 最終為用戶端所有,並繫結至 fidl::Binding 已透過伺服器的實作方式初始化。fidl::Binding 是類別 來自採用 FIDL 通訊協定實作項目和管道的 FIDL 執行階段 然後監聽管道上收到的要求接著解碼 將要求分派給伺服器類別的正確方法,並 將所有回應寫回用戶端。收聽 Podcast 節目的主要方式, 設定非同步迴圈的傳入要求。

如要進一步瞭解完整流程,請參閱 開放通訊協定的生命週期

新增依附元件

這個新程式碼需要下列額外依附元件:

  • "//sdk/lib/async-loop:async-loop-cpp"」和 "//sdk/lib/async-loop:async-loop-default":這些程式庫包含 非同步迴圈程式碼
  • "//sdk/lib/sys/cpp":元件架構 C++ 執行階段,其中包含 以及用於與元件環境互動的公用程式程式碼
  1. 將程式庫目標新增為 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",
      ]
    }
    
  2. 請在 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