必要條件
本教學課程以 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
。資訊清單中的二進位檔名稱必須與executable
中定義的BUILD.gn
輸出名稱相符。{ 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 上的軟體發布單位。在本例中,套件包含用戶端和伺服器元件,以及 realm 元件,用於宣告適當的功能和路徑。
# 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
通訊協定的執行個體。本教學課程結尾的元件會執行下列動作:
<0
初始化用戶端
如要向伺服器發出 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;
您也可以使用雙大括號語法,這是 natural structs 和表格支援的指定初始化樣式:
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;
}
使用線路網域物件撥打電話
上述教學課程會使用自然網域物件進行用戶端呼叫:每次呼叫都會耗用以自然網域物件表示的要求訊息,並傳回自然網域物件中的回覆。最佳化效能和堆積分配時,可使用線路網域物件發出呼叫。如要這麼做,請在進行呼叫時使用的取消參照運算子 (即 client.wire()->EchoString(...)
) 前插入 .wire()
。
使用以下線路類型進行 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
能力從伺服器路由至用戶端。本教學課程提供 realm 元件,用於宣告適當的功能和路徑。
設定建構作業,以便納入提供的套件,其中包含 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
僅限 Wire 網域物件的用戶端
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; } };
實作 Wire 事件處理常式:
// 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 結帳頁面 //examples/fidl/cpp/client_sync/wire
。