前提条件
本教程假定您熟悉如何编写和运行 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" }, ], }
创建组件后,请务必将其添加到 build 配置:
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", "//src/lib/fuchsia-component", "//src/lib/zircon/rust:fuchsia-zircon", "//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; 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::MonotonicTime::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::MonotonicTime::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::MonotonicTime::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::MonotonicTime::INFINITE).context("error receiving events")?;
println!("Received OnString event for string {:?}", response);
Ok(())
}
connect_channel_to_service
会将提供的通道端绑定到指定的
服务。在后台,此调用会触发在客户端上启动的一系列事件,并通过上一个教程中的服务器代码进行跟踪:
- 向组件框架发出请求,其中包含要连接的服务的名称,以及
渠道的另一端。服务名称是使用
SERVICE_NAME
隐式获取的 为EchoMarker
模板参数的值,类似于在服务器上确定服务路径的方式 end 的值。 - 此客户端对象是从
connect_to_protocol
返回的。
在后台,对组件框架的请求会路由到服务器:
- 在服务器进程中收到此请求后
它会唤醒
async::Executor
执行器,并告知其ServiceFs
任务现在可以 进度,应该运行。 ServiceFs
被唤醒,在进程的启动句柄上看到可用请求,并且 在(service_name, service_startup_func)
列表中查找所请求服务的名称 通过调用add_service
、add_fidl_service
等方法提供。如果匹配的service_name
它使用提供的通道调用service_startup_func
,以连接到新服务。- 使用
RequestStream
调用IncomingService::Echo
Echo
FIDL 协议的(类型化通道),该协议已在add_fidl_service
中注册。通过 传入的请求渠道存储在IncomingService::Echo
中,并添加到 传入请求。for_each_concurrent
将ServiceFs
转换为类型的Stream
IncomingService
。系统会为流中的每个条目运行处理程序,该处理程序将与传入的 请求和分派给run_echo_server
。通过对 的每次调用生成的 Future 当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::MonotonicTime::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::MonotonicTime::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
功能从
从服务器发送到客户端在本教程中,
领域
组件是
来声明相应的功能和路线。
配置 build,使其包含提供的软件包,其中包含 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