在 Rust 中實作 FIDL 用戶端

必要條件

本教學課程假設您已熟悉如何編寫及執行 Fuchsia 並實作 FIDL 伺服器,兩者都包含在 FIDL 伺服器教學課程。如需完整的 FIDL 教學課程,請參閱 總覽

總覽

本教學課程將實作 FIDL 通訊協定的用戶端並執行 針對上一個教學課程中建立的伺服器執行測試。這個架構中的用戶端 以及非同步教學課程我們也提供替代的教學課程 同步用戶端。

若您想要自行編寫程式碼,請刪除下列目錄:

rm -r examples/fidl/rust/client/*

建立元件

examples/fidl/rust/client 建立新的元件專案:

  1. main() 函式新增至 examples/fidl/rust/client/src/main.rs

    fn main() {
      println!("Hello, world!");
    }
    
  2. examples/fidl/rust/client/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"
      edition = "2021"
    
      sources = [ "src/main.rs" ]
    }
    
    fuchsia_component("echo-client") {
      component_name = "echo_client"
      manifest = "meta/client.cml"
      deps = [ ":bin" ]
    }
    
  3. examples/fidl/rust/client/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",
        },
    
        // Capabilities used by this component.
        use: [
            { protocol: "fuchsia.examples.Echo" },
        ],
    }
    
    
  4. 建立元件後,請確定能將其新增至 建構設定:

    fx set core.x64 --with //examples/fidl/rust/client:echo-client
  5. 建構 Fuchsia 映像檔:

    fx build

編輯 GN 依附元件

  1. 將下列依附元件新增至 rustc_binary

      deps = [
        "//examples/fidl/fuchsia.examples:fuchsia.examples_rust",
        "//src/lib/fuchsia",
        "//src/lib/fuchsia-component",
        "//third_party/rust_crates:anyhow",
        "//third_party/rust_crates:futures",
      ]
    
    
  2. 然後在 main.rs 中匯入:

    use anyhow::{Context as _, Error};
    use fidl_fuchsia_examples::{EchoEvent, EchoMarker};
    use fuchsia_component::client::connect_to_protocol;
    use futures::prelude::*;
    

如要瞭解這些依附元件,請參閱伺服器教學課程

連線至伺服器

本節中的步驟說明如何在 main() 函式中新增程式碼 這個網路可讓用戶端連上伺服器,並向伺服器發出要求。

連線至伺服器

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    // Connect to the Echo protocol, which is assumed to be in the component's environment
    let echo = connect_to_protocol::<EchoMarker>().context("Failed to connect to echo service")?;

    // Make an EchoString request and wait for the response
    let res = echo.echo_string("hello").await?;
    println!("response: {:?}", res);

    // Make a SendString request
    echo.send_string("hi")?;
    // Wait for a single OnString event
    let EchoEvent::OnString { response } =
        echo.take_event_stream().next().await.context("error receiving events")??;
    println!("Received OnString event for string {:?}", response);

    Ok(())
}

實際上,此呼叫會觸發在用戶端上開始的一系列事件, 先前教學課程中的伺服器程式碼

  • 初始化用戶端物件與管道。用戶端物件會繫結至 頻道。
  • 向元件架構提出要求,該架構包含要連線的服務名稱,以及 管道的其他部分服務名稱是使用 SERVICE_NAME 以隱含方式取得 EchoMarker 範本引數,這類似於伺服器在伺服器上決定服務路徑的方式 結尾。
  • 這個用戶端物件是從 connect_to_protocol 傳回。

在背景中,傳送至元件架構的要求會轉送至伺服器:

  • 伺服器程序收到這項要求後, 它會喚醒 async::Executor 執行程式,並通知 ServiceFs 工作現在可以 並應該執行
  • ServiceFs 會喚醒、查看程序啟動控點中的可用要求,以及 在 (service_name, service_startup_func) 清單中查詢所要求服務的名稱 透過呼叫 add_serviceadd_fidl_service 等條件。如果相符的 service_name 存在,則會使用提供的管道呼叫 service_startup_func,以連線至新服務。
  • 系統會使用 RequestStream 呼叫 IncomingService::Echoadd_fidl_service 註冊的 Echo FIDL 通訊協定 (類型管道)。 傳入的要求管道會儲存在 IncomingService::Echo 中,並新增至 for_each_concurrent 會將 ServiceFs 消耗成類型的 Stream IncomingService。系統會針對串流中的每個項目執行處理常式,以比對傳入 要求及分派給 run_echo_server。每個呼叫所產生的 Future ServiceFs 串流為 await 時,會並行執行 run_echo_server
  • 在管道上傳送要求時,系統會可讀取 Echo 服務管道, 會在 run_echo_server 主體中喚醒非同步程式碼。

將要求傳送至伺服器

程式碼會向伺服器發出兩項要求:

  • EchoString 要求
  • SendString 要求
#[fuchsia::main]
async fn main() -> Result<(), Error> {
    // Connect to the Echo protocol, which is assumed to be in the component's environment
    let echo = connect_to_protocol::<EchoMarker>().context("Failed to connect to echo service")?;

    // Make an EchoString request and wait for the response
    let res = echo.echo_string("hello").await?;
    println!("response: {:?}", res);

    // Make a SendString request
    echo.send_string("hi")?;
    // Wait for a single OnString event
    let EchoEvent::OnString { response } =
        echo.take_event_stream().next().await.context("error receiving events")??;
    println!("Received OnString event for string {:?}", response);

    Ok(())
}

EchoString 的呼叫會傳回 Future,也就是伺服器傳回的回應。 如果傳送要求時發生錯誤,傳回的 Future 將解析為錯誤,或 接收回應 (例如將訊息解碼時,或系統收到回應時)。

另一方面,對 SendString 的呼叫會傳回 Result,因為這是火災而忘記 方法。如果傳送要求時發生問題,這個方法呼叫會傳回錯誤。

繫結參考資料說明瞭這些 Proxy 方法的產生方式,以及 Fuchsia rustdoc 提供所產生 FIDL Crate 的說明文件。

處理傳入事件

接著,程式碼會等待伺服器傳回單一 OnString 事件:

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    // Connect to the Echo protocol, which is assumed to be in the component's environment
    let echo = connect_to_protocol::<EchoMarker>().context("Failed to connect to echo service")?;

    // Make an EchoString request and wait for the response
    let res = echo.echo_string("hello").await?;
    println!("response: {:?}", res);

    // Make a SendString request
    echo.send_string("hi")?;
    // Wait for a single OnString event
    let EchoEvent::OnString { response } =
        echo.take_event_stream().next().await.context("error receiving events")??;
    println!("Received OnString event for string {:?}", response);

    Ok(())
}

方法是從用戶端物件取得事件串流,然後等待 以單一事件做為依據

執行用戶端

為了讓用戶端和伺服器使用 Echo 通訊協定進行通訊, 元件架構必須從 fuchsia.examples.Echo 伺服器傳送至用戶端在本教學課程中 運作範圍 元件是 來宣告適當的功能和路徑

  1. 設定您的版本以包含 echo 領域、伺服器和用戶端:

    fx set core.x64 --with //examples/fidl/rust:echo-rust-client
  2. 建構 Fuchsia 映像檔:

    fx build
  3. 執行 echo_realm 元件。這項操作會建立用戶端和伺服器元件 以及轉送功能:

    ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-rust-client#meta/echo_realm.cm
  4. 啟動 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