必要條件
本教學課程是以編譯 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" ] }
為了讓伺服器元件正常執行,在 Pod 中 定義:
- 專為在 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()
函式的 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++ 執行階段,其中包含 以及用於與元件環境互動的公用程式程式碼
將程式庫目標新增為
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