在 Rust 中實作同步 FIDL 用戶端

必要條件

本教學課程假設您已熟悉如何編寫和執行 Fuchsia 元件,以及如何實作 FIDL 伺服器,詳情請參閱 FIDL 伺服器教學課程。如需完整的 FIDL 教學課程,請參閱總覽

總覽

本教學課程將實作 FIDL 通訊協定的用戶端,並以上一個教學課程中建立的伺服器執行該用戶端。本教學課程中的用戶端處於同步狀態。另有非同步用戶端的替代教學課程

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

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

建立元件

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

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

    fn main() {
      println!("Hello, world!");
    }
    
  2. 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" ]
    }
    
  3. 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" },
        ],
    }
    
    
  4. 建立元件後,請確認可將元件加入建構設定:

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

    fx build
    

編輯 GN 依附元件

  1. rustc_binary 中新增下列依附元件:

      deps = [
        "//examples/fidl/fuchsia.examples:fuchsia.examples_rust",
        "//src/lib/fuchsia-component",
        "//src/lib/zircon/rust:fuchsia-zircon",
        "//third_party/rust_crates:anyhow",
      ]
    
    
  2. 然後將其匯入 main.rs

    use anyhow::{Context as _, Error};
    use fidl_fuchsia_examples::{EchoEvent, EchoMarker};
    use fuchsia_component::client::connect_to_protocol_sync;
    use fuchsia_zircon as zx;
    

如需這些依附元件的說明,請參閱伺服器教學課程

新的依附元件是 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::Time::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::Time::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::Time::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::Time::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_serviceadd_fidl_service 等方式提供的 (service_name, service_startup_func) 清單中查詢所要求服務的名稱。如果相符的 service_name 存在,就會使用提供的管道呼叫 service_startup_func,連線至新服務。
  • 系統會使用使用 add_fidl_service 註冊的 Echo FIDL 通訊協定的 RequestStream (類型管道) 呼叫 IncomingService::Echo。傳入的要求管道會儲存在 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::Time::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::Time::INFINITE).context("error receiving events")?;
    println!("Received OnString event for string {:?}", response);

    Ok(())
}

echo_string 發出的呼叫會封鎖,直到從伺服器收到回應為止,因此該呼叫會使用逾時引數做為最後一個參數。

另一方面,由於 SendString 沒有回應,對 send_string 的呼叫並沒有逾時參數。使用目前的伺服器實作時,系統接收到這項要求後,會把 OnString 事件傳送至用戶端。不過,同步的 Rust 繫結無法處理事件。

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

執行用戶端

為了讓用戶端和伺服器能使用 Echo 通訊協定進行通訊,元件架構必須將 fuchsia.examples.Echo 能力從伺服器轉送至用戶端。

  1. 設定建構以納入提供的套件,當中包含 echo 領域、伺服器和用戶端:

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

    fx build
    
  3. 執行 echo_realm 元件。這會建立用戶端和伺服器元件執行個體,並轉送功能:

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