必要條件
本教學課程的設計是以 FIDL 伺服器教學課程為基礎。如需 FIDL 完整教學課程,請參閱總覽。
總覽
本教學課程會實作 FIDL 通訊協定的用戶端,並針對先前教學課程中建立的伺服器執行用戶端。本教學課程中的用戶端是同步的。我們也提供其他教學課程,適用於非同步用戶端。
用戶端範例的結構
本教學課程附帶的範例程式碼位於 Fuchsia 檢出的 //examples/fidl/cpp/client_sync
中。其中包含用戶端元件和內含的套件。如要進一步瞭解如何建構元件,請參閱「建構元件」。
如要啟動及執行用戶端元件,//examples/fidl/cpp/client_sync/BUILD.gn
中定義了三個目標:
用戶端的原始執行檔。這會產生具有指定輸出名稱的二進位檔,可在 Fuchsia 上執行:
executable("bin") { output_name = "fidl_echo_cpp_client_sync" sources = [ "main.cc" ] deps = [ "//examples/fidl/fuchsia.examples:fuchsia.examples_cpp", # This library is used to log messages. "//sdk/lib/syslog/cpp", # This library is used to consume capabilities, e.g. protocols, # from the component's incoming directory. "//sdk/lib/component/incoming/cpp", ] }
已設定為執行用戶端可執行檔的元件。元件為 Fuchsia 軟體執行單位。元件會由其資訊清單檔案描述。在這種情況下,
meta/client.cml
會將echo-client
設為可執行的元件,在:bin
中執行fidl_echo_cpp_client_sync
。fuchsia_component("echo-client") { component_name = "echo_client" manifest = "meta/client.cml" deps = [ ":bin" ] }
伺服器元件資訊清單位於
//examples/fidl/cpp/client_sync/meta/client.cml
。資訊清單中的二進位名稱必須與BUILD.gn
中定義的executable
輸出名稱相符。{ 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_cpp_client_sync", }, // Capabilities used by this component. use: [ { protocol: "fuchsia.examples.Echo" }, ], }
接著,元件會放入套件中,也就是 Fuchsia 的軟體分配單位。在這種情況下,套件會包含用戶端和伺服器元件,並且
# C++ sync client and server example package fuchsia_package("echo-cpp-client-sync") { deps = [ ":echo-client", "//examples/fidl/cpp/server:echo-server", "//examples/fidl/echo-realm:echo_realm", ] }
建構用戶端
將用戶端新增至建構設定。這項操作只需執行一次:
fx set core.x64 --with //examples/fidl/cpp/client_sync
建構用戶端:
fx build examples/fidl/cpp/client_sync
連線至通訊協定
在主函式中,用戶端元件會在其
fuchsia.examples/Echo
int main(int argc, const char** argv) {
// Connect to the |fuchsia.examples/Echo| protocol inside the component's
// namespace. This can fail so it's wrapped in a |zx::result| and it must be
// checked for errors.
zx::result client_end = component::Connect<fuchsia_examples::Echo>();
if (!client_end.is_ok()) {
FX_LOGS(ERROR) << "Synchronous error when connecting to the |Echo| protocol: "
<< client_end.status_string();
return -1;
}
// ...
同時,元件管理工具會將要求導向伺服器元件。伺服器教學課程中實作的 伺服器處理常式會搭配伺服器端點呼叫,將管道繫結至伺服器實作項目。
這裡要注意的重要事項是,這個程式碼會假設元件的命名空間已包含 Echo
通訊協定的例項。在教學課程結尾執行通訊協定時,會建立一個
初始化用戶端
如要向伺服器提出 Echo
要求,請使用上一個步驟中的用戶端端點,初始化用戶端。
fidl::SyncClient client{std::move(*client_end)};
發出 FIDL 呼叫
執行 FIDL 呼叫的方法會在解除參照運算子之後公開,例如 FIDL 呼叫看起來像 client->EchoString(...)
。
以下兩種呼叫 (例如 EchoString
) 會接受要求物件,並傳回表示成功或失敗的結果物件:
fidl::Result result = client->EchoString({"hello"});
// Check if the FIDL call succeeded or not.
if (!result.is_ok()) {
// If the call failed, log the error, and quit the program.
// Production code should do more graceful error handling depending
// on the situation.
FX_LOGS(ERROR) << "EchoString failed: " << result.error_value();
return -1;
}
const std::string& reply_string = result->response();
FX_LOGS(INFO) << "Got response: " << reply_string;
您也可以使用自然結構和資料表支援的指定初始化樣式雙大括號語法:
fidl::Result result = client->EchoString({{.value = "hello"}});
單向 SendString
呼叫沒有回應。傳回的結果代表傳送要求時發生的任何錯誤。
fit::result<fidl::Error> result = client->SendString({"hi"});
if (!result.is_ok()) {
FX_LOGS(ERROR) << "SendString failed: " << result.error_value();
return -1;
}
處理事件
定義事件處理常式:
// Define the event handler implementation for the client.
//
// The event handler should be an object that implements
// |fidl::SyncEventHandler<Echo>|, and override all pure virtual methods
// in that class corresponding to the events offered by the protocol.
class EventHandler : public fidl::SyncEventHandler<fuchsia_examples::Echo> {
public:
EventHandler() = default;
void OnString(fidl::Event<fuchsia_examples::Echo::OnString>& event) override {
const std::string& reply_string = event.response();
FX_LOGS(INFO) << "Got event: " << reply_string;
}
};
呼叫 client.HandleOneEvent
以阻斷,直到收到事件為止。如果系統已辨識並成功解碼事件,HandleOneEvent
會傳回 fidl::Status::Ok()
。否則,會傳回適當的錯誤。如果伺服器使用碑文關閉連線,系統會傳回碑文中包含的狀態。
// Block to receive exactly one event from the server, which is handled using
// the event handler defined above.
EventHandler event_handler;
fidl::Status status = client.HandleOneEvent(event_handler);
if (!status.ok()) {
FX_LOGS(ERROR) << "HandleOneEvent failed: " << status.error();
return -1;
}
使用線路網域物件撥打電話
上述教學課程會使用自然網域物件建立用戶端呼叫:每個呼叫都會使用自然網域物件代表要求訊息,並以自然網域物件傳回回覆。在針對效能和堆積配置進行最佳化時,您可以使用線路網域物件進行呼叫。方法是在發出呼叫時使用的解除參照運算子之前插入 .wire()
,例如 client.wire()->EchoString(...)
。
使用線路類型建立 EchoString
雙向呼叫:
fidl::WireResult result = client.wire()->EchoString("hello");
if (!result.ok()) {
FX_LOGS(ERROR) << "EchoString failed: " << result.error();
return -1;
}
FX_LOGS(INFO) << "Got response: " << result->response.get();
使用線路類型發出 SendString
單向呼叫:
fidl::Status wire_result = client.wire()->SendString("hi");
if (!wire_result.ok()) {
FX_LOGS(ERROR) << "SendString failed: " << wire_result.error();
return -1;
}
在線用戶端呼叫中使用的相關類別和函式,與自然用戶端呼叫中使用的類別和函式形狀相似。呼叫其他類別或函式時,線路對應項目通常會加上 Wire
前置字元。指標和參照以及引數結構也有差異:
EchoString
方法會採用自然網域物件,並接受單一引數,也就是要求網域物件:fidl::Result result = client->EchoString({{.value = "hello"}});
如果要求酬載是結構體,則使用線路網域物件的
EchoString
方法會將要求主體中的結構體欄位清單扁平化為個別引數 (此處為單一fidl::StringView
引數):fidl::WireResult result = client.wire()->EchoString("hello");
兩種自然呼叫都會傳回
fidl::Result<Method>
:fidl::Result result = client->EchoString({{.value = "hello"}}); if (!result.is_ok()) { FX_LOGS(ERROR) << "EchoString failed: " << result.error_value(); return -1; } const std::string& reply_string = result->response(); FX_LOGS(INFO) << "Got response: " << reply_string;
- 如要檢查成功或錯誤,請使用
is_ok()
或is_error()
方法。 - 如要之後存取回應酬載,請使用
value()
或->
。 - 您可以移出結果或酬載,因為這些類型都會實作階層式物件擁有權。
雙向電線呼叫會傳回
fidl::WireResult<Method>
:fidl::WireResult result = client.wire()->EchoString("hello"); if (!result.ok()) { FX_LOGS(ERROR) << "EchoString failed: " << result.error(); return -1; } FX_LOGS(INFO) << "Got response: " << result->response.get();
- 如要確認是否成功,請使用
ok()
方法。 - 如要之後存取回應酬載,請使用
value()
或->
。 - 您無法移動結果物件。
- 如要檢查成功或錯誤,請使用
其中一種方式呼叫也會以自然情況下取得整個要求網域物件,並將要求結構欄位簡化為傳輸案例中的個別引數:
// Make a SendString call using natural types. fit::result<fidl::Error> result = client->SendString({"hi"}); // Make a SendString call using wire types. fidl::Status wire_result = client.wire()->SendString("hi");
執行用戶端
為了讓用戶端和伺服器使用 Echo
通訊協定進行通訊,元件架構必須將 fuchsia.examples.Echo
能力從伺服器路由至用戶端。在本教學課程中,我們會使用
設定建構作業,加入提供的套件,其中包含回音領域、伺服器和用戶端:
fx set core.x64 --with //examples/fidl/cpp/client_sync
建構 Fuchsia 映像檔:
fx build
執行
echo_realm
元件。這會建立用戶端和伺服器元件執行個體,並將功能導向:ffx component run /core/ffx-laboratory:echo-client fuchsia-pkg://fuchsia.com/echo-cpp-client-sync#meta/echo_realm.cm
啟動
echo_client
執行個體:ffx component start /core/ffx-laboratory:echo_realm/echo_client
當用戶端嘗試連線至 Echo
通訊協定時,伺服器元件就會啟動。您應該會在裝置記錄 (ffx log
) 中看到類似以下的輸出內容:
[echo_server][][I] Running echo server
[echo_client][I] Got response: hello
[echo_client][I] Got event: hi
[echo_client][I] Got response: hello
[echo_server][I] Client disconnected
終止領域元件,停止執行並清理元件例項:
ffx component destroy /core/ffx-laboratory:echo_realm
僅限線網域物件用戶端
fidl::SyncClient
支援使用自然網域物件和線纜網域物件進行呼叫。如果您只需要使用線路網域物件,可以建立 WireSyncClient
,將等效的方法呼叫介面公開為在 fidl::SyncClient
上呼叫 client.wire()
所取得的子集。
WireSyncClient
的建立方式與 SyncClient
相同:
fidl::WireSyncClient client(std::move(*client_end));
fidl::SyncClient
一律會以自然網域物件的形式,向使用者公開收到的事件。另一方面,fidl::WireSyncClient
會以傳輸網域物件的形式公開接收的事件。為此,傳遞至 WireSyncClient
的事件處理常式需要實作 fidl::WireSyncEventHandler<Protocol>
。
實作自然事件處理常式:
// Define the event handler implementation for the client. // // The event handler should be an object that implements // |fidl::SyncEventHandler<Echo>|, and override all pure virtual methods // in that class corresponding to the events offered by the protocol. class EventHandler : public fidl::SyncEventHandler<fuchsia_examples::Echo> { public: EventHandler() = default; void OnString(fidl::Event<fuchsia_examples::Echo::OnString>& event) override { const std::string& reply_string = event.response(); FX_LOGS(INFO) << "Got event: " << reply_string; } };
實作線路事件處理常式:
// Define the event handler implementation for the client. // // The event handler should be an object that implements // |fidl::WireSyncEventHandler<Echo>|, and override all pure virtual methods // in that class corresponding to the events offered by the protocol. class EventHandler : public fidl::WireSyncEventHandler<fuchsia_examples::Echo> { public: EventHandler() = default; void OnString(fidl::WireEvent<fuchsia_examples::Echo::OnString>* event) override { FX_LOGS(INFO) << "Got event: " << event->response.get(); } };
您可以在 Fuchsia Checkout 的結帳頁面 (//examples/fidl/cpp/client_sync/wire
) 中找到使用電線用戶端的完整程式碼範例。