实现 FIDL 客户端

前提条件

本教程基于 FIDL 服务器教程构建。如需查看完整的 FIDL 教程集,请参阅概览

概览

本教程针对 FIDL 协议实现了一个客户端,并针对在上一个教程中创建的服务器运行该协议。本教程中的客户端是同步的。我们提供了一个替代教程,介绍了如何使用异步客户端。

如果要自行编写代码,请删除以下目录:

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

创建组件

examples/fidl/hlcpp/client_sync 中创建一个新的组件项目:

  1. main() 函数添加到 examples/fidl/hlcpp/client_sync/main.cc

    int main(int argc, const char** argv) {
      printf("Hello, world!\n");
      return 0;
    }
    
  2. examples/fidl/hlcpp/client_sync/BUILD.gn 中为客户端声明目标:

    import("//build/components.gni")
    
    
    # Declare an executable for the client.
    executable("bin") {
      output_name = "fidl_echo_hlcpp_client_sync"
      sources = [ "main.cc" ]
    }
    
    fuchsia_component("echo-client") {
      component_name = "echo_client"
      manifest = "meta/client.cml"
      deps = [ ":bin" ]
    }
    
  3. examples/fidl/hlcpp/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_hlcpp_client_sync",
        },
    
        // Capabilities used by this component.
        use: [
            { protocol: "fuchsia.examples.Echo" },
        ],
    }
    
    
  4. 创建组件后,请确保可以将其添加到 build 配置中:

    fx set core.x64 --with //examples/fidl/hlcpp/client_sync:echo-client
    
  5. 构建 Fuchsia 映像:

    fx build
    

修改 GN 依赖项

  1. 添加以下依赖项:

      deps = [
        "//examples/fidl/fuchsia.examples:fuchsia.examples_hlcpp",
        "//sdk/lib/sys/cpp",
      ]
    
    
  2. 然后,将它们添加到 main.cc 中:

    #include <fuchsia/examples/cpp/fidl.h>
    #include <lib/sys/cpp/component_context.h>
    

    服务器教程中介绍了添加这些依赖项的原因。

连接到服务器

本部分为连接到服务器并向服务器发出请求的 main() 函数添加代码。

初始化代理类

然后,代码会为 Echo 协议创建一个代理类,并将其连接到服务器。在 FIDL 环境中,代理指定由 FIDL 绑定生成的代码,使用户能够对服务器进行远程过程调用。在 HLCPP 中,代理采用类的形式,其中包含与每个 FIDL 协议方法相对应的方法。

int main(int argc, const char** argv) {
  fuchsia::examples::EchoSyncPtr echo_proxy;
  auto context = sys::ComponentContext::Create();
  context->svc()->Connect(echo_proxy.NewRequest());

  ZX_ASSERT(echo_proxy->SendString("hi") == ZX_OK);
  std::string response;
  ZX_ASSERT(echo_proxy->EchoString("hello", &response) == ZX_OK);
  printf("Got response: %s\n", response.c_str());

  // TODO(fcz): this currently does not pass on CQ
  // return response == "hello" ? 0 : 1;
  return 0;
}
  • fuchsia::examples::EchoSyncPtr 是绑定生成的 fidl::SynchronousInterfaceRequest<fuchsia::examples::Echo> 的别名。此类将通过其绑定的通道代理 Echo 协议的请求。
  • 该代码会调用 EchoSyncPtr::NewRequest(),后者会创建一个通道,将类绑定到一个端点,并返回另一个端点
  • 所返回的值会传递给 sys::ServiceDirectory::Connect()
    • 与服务器端对 context->out()->AddPublicService() 的调用类似,Connect 在这里有一个隐式第二个参数,即协议名称 ("fuchsia.examples.Echo")。上一教程中定义的处理程序的输入便来自于此处:客户端将其传递给 Connect,然后 Connect 将其传递给处理程序。

这里要注意的重要一点是,此代码假定 /svc 已包含 Echo 协议的实例。默认情况下,情况并非如此,因为组件框架提供了沙盒化功能。

向服务器发送请求

该代码会向服务器发出两个请求:

  • EchoString 请求
  • SendString 请求
int main(int argc, const char** argv) {
  fuchsia::examples::EchoSyncPtr echo_proxy;
  auto context = sys::ComponentContext::Create();
  context->svc()->Connect(echo_proxy.NewRequest());

  ZX_ASSERT(echo_proxy->SendString("hi") == ZX_OK);
  std::string response;
  ZX_ASSERT(echo_proxy->EchoString("hello", &response) == ZX_OK);
  printf("Got response: %s\n", response.c_str());

  // TODO(fcz): this currently does not pass on CQ
  // return response == "hello" ? 0 : 1;
  return 0;
}

对于 EchoString,代码会为每个响应参数传入一个指针(在本例中,EchoString 方法只有一个响应参数),该指针会使用来自服务器的响应进行写入,但这不适用于 SendString,因为它是一种火花式方法。对 EchoString 的调用将一直处于阻塞状态,直到收到来自服务器的消息。这两种方法都会返回一个 zx_status_t,用于指示方法调用的结果。

虽然服务器实现会响应 SendString 请求发送 OnString 事件,但同步绑定不提供处理此事件的方法。

运行客户端

为了让客户端和服务器能够使用 Echo 协议进行通信,组件框架必须将 fuchsia.examples.Echo capability 从服务器路由到客户端。在本教程中,我们提供了一个 realm 组件来声明适当的 capability 和路由。

探索 Realm 组件的完整源代码
  1. 将 build 配置为包含包含 echo 领域、服务器和客户端的提供的软件包:

    fx set core.x64 --with //examples/fidl/hlcpp:echo-hlcpp-client-sync
    
  2. 构建 Fuchsia 映像:

    fx build
    
  3. 运行 echo_realm 组件。这会创建客户端和服务器组件实例并路由功能:

    ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-hlcpp-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] Running echo server
[echo_client][][I] Got response: hello

终止 Realm 组件以停止执行并清理组件实例:

ffx component destroy /core/ffx-laboratory:echo_realm