必要條件
本教學課程假設您熟悉編寫及執行 Fuchsia 元件,以及實作 FIDL 伺服器,這兩項內容皆已在 FIDL 伺服器教學課程中介紹。如需 FIDL 的完整教學課程,請參閱總覽。
總覽
本教學課程會實作 FIDL 通訊協定的用戶端,並針對先前教學課程中建立的伺服器執行用戶端。本教學課程中的用戶端是同步的。我們也提供其他教學課程,可用於非同步用戶端。
如果您想自行編寫程式碼,請刪除下列目錄:
rm -r examples/fidl/rust/client_sync/*建立元件
在 examples/fidl/rust/client_sync 中建立新的元件專案:
- 將 - main()函式新增至- examples/fidl/rust/client_sync/src/main.rs:- fn main() { println!("Hello, world!"); }
- 在 - examples/fidl/rust/client_sync/BUILD.gn中為用戶端宣告目標:- import("//build/components.gni") import("//build/rust/rustc_binary.gni") # Declare an executable for the client. rustc_binary("bin") { name = "fidl_echo_rust_client_sync" edition = "2021" sources = [ "src/main.rs" ] } fuchsia_component("echo-client") { component_name = "echo_client" manifest = "meta/client.cml" deps = [ ":bin" ] }
- 在 - examples/fidl/rust/client_sync/meta/client.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_rust_client_sync", }, // Capabilities used by this component. use: [ { protocol: "fuchsia.examples.Echo" }, ], }
- 建立元件後,請確認您可以將其新增至建構設定: - fx set core.x64 --with //examples/fidl/rust/client_sync:echo-client
- 建構 Fuchsia 映像檔: - fx build
編輯 GN 依附元件
- 將下列依附元件新增至 - rustc_binary:- deps = [ "//examples/fidl/fuchsia.examples:fuchsia.examples_rust", "//sdk/rust/zx", "//src/lib/fuchsia-component", "//third_party/rust_crates:anyhow", ]
- 然後在 - main.rs中匯入:- use anyhow::{Context as _, Error}; use fidl_fuchsia_examples::{EchoEvent, EchoMarker}; use fuchsia_component::client::connect_to_protocol_sync;
伺服器教學課程會說明這些依附元件。
唯一的新依附元件是 fuchsia-zircon,這是一個 crate,其中包含用於建立 Zircon 核心系統呼叫的型別安全繫結。在本例中,crate 用於建立管道。
連線至伺服器
本節的步驟說明如何在 main() 函式中新增程式碼,讓用戶端與伺服器連線並向其提出要求。
初始化管道
fn main() -> Result<(), Error> {
    // Connect to the Echo protocol, returning a synchronous proxy
    let echo =
        connect_to_protocol_sync::<EchoMarker>().context("Failed to connect to echo service")?;
    // Make an EchoString request, with no timeout for receiving the response
    let res = echo.echo_string("hello", zx::MonotonicInstant::INFINITE)?;
    println!("response: {:?}", res);
    // Make a SendString request
    echo.send_string("hi")?;
    // Wait for a single OnString event.
    let EchoEvent::OnString { response } =
        echo.wait_for_event(zx::MonotonicInstant::INFINITE).context("error receiving events")?;
    println!("Received OnString event for string {:?}", response);
    Ok(())
}
這個管道將用於用戶端與伺服器之間的通訊。
連線至伺服器
fn main() -> Result<(), Error> {
    // Connect to the Echo protocol, returning a synchronous proxy
    let echo =
        connect_to_protocol_sync::<EchoMarker>().context("Failed to connect to echo service")?;
    // Make an EchoString request, with no timeout for receiving the response
    let res = echo.echo_string("hello", zx::MonotonicInstant::INFINITE)?;
    println!("response: {:?}", res);
    // Make a SendString request
    echo.send_string("hi")?;
    // Wait for a single OnString event.
    let EchoEvent::OnString { response } =
        echo.wait_for_event(zx::MonotonicInstant::INFINITE).context("error receiving events")?;
    println!("Received OnString event for string {:?}", response);
    Ok(())
}
connect_channel_to_service 會將提供的管道端點繫結至指定的服務。實際上,這個呼叫會觸發一系列事件,這些事件會在用戶端開始,並追蹤先前教學課程中的伺服器程式碼:
- 向元件架構提出要求,其中包含要連線的服務名稱和管道的另一端。系統會使用 EchoMarker範本引數的SERVICE_NAME隱含取得服務名稱,這與在伺服器端決定服務路徑的方式類似。
- 這個用戶端物件會從 connect_to_protocol傳回。
在背景中,對元件架構的要求會轉送至伺服器:
- 在伺服器程序中收到這項要求時,系統會喚醒 async::Executor執行器,並告知ServiceFs工作現在可以取得進度,且應執行。
- ServiceFs會喚醒,並查看程序的啟動句柄上是否有可用的要求,然後在透過呼叫- add_service、- add_fidl_service等提供的- (service_name, service_startup_func)清單中查詢要求服務的名稱。如果有相符的- service_name,系統會使用提供的管道呼叫- service_startup_func,以連線至新服務。
- IncomingService::Echo會使用- add_fidl_service註冊的- EchoFIDL 通訊協定的- RequestStream(類型管道) 呼叫。傳入的要求管道會儲存在- IncomingService::Echo中,並新增至傳入要求的串流。- for_each_concurrent會將- ServiceFs轉換為- IncomingService類型的- Stream。會為串流中的每個項目執行處理程序,該程序會比對傳入的要求,並將結果分派至- run_echo_server。當- ServiceFs串流遭到- await時,每個對- run_echo_server的呼叫所產生的結果未來會同時執行。
- 當要求透過管道傳送時,Echo服務的管道就會變得可讀,這會喚醒run_echo_server主體中的非同步程式碼。
將要求傳送至伺服器
程式碼會向伺服器提出兩項要求:
- EchoString要求
- SendString要求
fn main() -> Result<(), Error> {
    // Connect to the Echo protocol, returning a synchronous proxy
    let echo =
        connect_to_protocol_sync::<EchoMarker>().context("Failed to connect to echo service")?;
    // Make an EchoString request, with no timeout for receiving the response
    let res = echo.echo_string("hello", zx::MonotonicInstant::INFINITE)?;
    println!("response: {:?}", res);
    // Make a SendString request
    echo.send_string("hi")?;
    // Wait for a single OnString event.
    let EchoEvent::OnString { response } =
        echo.wait_for_event(zx::MonotonicInstant::INFINITE).context("error receiving events")?;
    println!("Received OnString event for string {:?}", response);
    Ok(())
}
呼叫 echo_string 會在收到伺服器的回應前阻斷,因此會將逾時引數做為最後一個參數。
另一方面,由於 SendString 沒有回應,因此對 send_string 的呼叫沒有逾時參數。在目前的伺服器導入作業中,系統會在收到這項要求後,將 OnString 事件傳送至用戶端。不過,同步的 Rust 繫結不支援事件處理。
繫結參考資料說明這些方法的產生方式,而 Fuchsia rustdoc 則包含產生的 FIDL 箱函說明文件。
執行用戶端
為了讓用戶端和伺服器使用 Echo 通訊協定進行通訊,元件架構必須將 fuchsia.examples.Echo 能力從伺服器路由至用戶端。在本教學課程中,我們會使用
- 設定建構作業,以便納入提供的套件,其中包含回音領域、伺服器和用戶端: - fx set core.x64 --with //examples/fidl/rust:echo-rust-client-sync
- 建構 Fuchsia 映像檔: - fx build
- 執行 - echo_realm元件。這會建立用戶端和伺服器元件例項,並將功能路由:- ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-rust-client-sync#meta/echo_realm.cm
- 啟動 - echo_client執行個體:- ffx component start /core/ffx-laboratory:echo_realm/echo_client
當用戶端嘗試連線至 Echo 通訊協定時,伺服器元件就會啟動。您應該會在裝置記錄 (ffx log) 中看到類似以下的輸出內容:
[echo_server][][I] Listening for incoming connections...
[echo_server][][I] Received EchoString request for string "hello"
[echo_server][][I] Response sent successfully
[echo_client][][I] response: "hello"
[echo_server][][I] Received SendString request for string "hi"
[echo_server][][I] Event sent successfully
[echo_client][][I] Received OnString event for string "hi"
終止領域元件,停止執行並清理元件例項:
ffx component destroy /core/ffx-laboratory:echo_realm