必要條件
本教學課程以 FIDL 伺服器教學課程為基礎。如需完整的 FIDL 教學課程,請參閱總覽。
總覽
本教學課程將實作 FIDL 通訊協定的用戶端,並以上一個教學課程中建立的伺服器執行該用戶端。本教學課程中的用戶端處於同步狀態。另有非同步用戶端的替代教學課程。
客戶範例結構
本教學課程隨附的程式碼範例位於 //examples/fidl/cpp/client_sync
的 Fuchsia 結帳頁面。其中包含用戶端元件及其包含的套件。如要進一步瞭解如何建構元件,請參閱「建構元件」。
如要讓用戶端元件啟動並執行,//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
連線至通訊協定
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()
。否則,系統會傳回適當的錯誤。如果伺服器以 Epitaph 關閉連線,系統會傳回 Epitaph 中包含的狀態。
// 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
能力從伺服器轉送至用戶端。
設定建構以納入提供的套件,當中包含 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 結帳頁面的 //examples/fidl/cpp/client_sync/wire
中找到使用接線用戶端的完整程式碼範例。