异步响应请求

前提条件

本教程基于入门教程

概览

本教程的完整示例代码位于 //examples/fidl/cpp/server_async_completer

在初始服务器教程中的 Echo 实现中,服务器代码使用补全程序响应了 EchoString 请求:

void EchoString(EchoStringRequest& request, EchoStringCompleter::Sync& completer) override {
  // Call |Reply| to reply synchronously with the request value.
  completer.Reply({{.response = request.value()}});
}

请注意,完成器的类型以 ::Sync 结尾。必须在处理脚本返回之前使用同步完成程序。强制执行此操作可进行优化,因为用于进行回复的记账元数据可以堆分配。

异步响应

在许多情况下,同步响应是不可行的。例如,回复可能需要其他异步调用的结果。如需异步响应,我们必须使用 ToAsync 从同步完成程序中获取异步完成程序:

EchoStringCompleter::Async async_completer = completer.ToAsync();

生成的 async_completer 会公开与 completer 相同的 API,但可以移除以供日后使用。

在示例代码中,服务器在延迟任务中进行回复,模拟了服务器必须先执行长时间运行的任务,然后才能发送响应的场景:

void EchoString(EchoStringRequest& request, EchoStringCompleter::Sync& completer) override {
  async::PostDelayedTask(
      dispatcher_,
      [value = request.value(), completer = completer.ToAsync()]() mutable {
        completer.Reply({{.response = value}});
      },
      zx::duration(ZX_SEC(1)));
}

服务器不会在从当前处理程序方法返回之前开始处理任何新请求。从 EchoString 返回后,服务器将监控端点以查找新的 FIDL 消息,同时将回复安排在 1 秒后发送。这意味着,如果客户端快速连续发送多个 EchoString 请求,我们可能会有同样多的并发异步延迟任务正在处理中。

在使用 Wire 网域对象的服务器中异步响应

当服务器使用线程域对象时,您可以使用相同的 ToAsync 操作,但要特别注意对象生命周期。具体而言,向方法处理程序提供的请求视图不拥有请求消息。如果异步任务需要在 EchoString 方法返回后使用请求参数,则需要将相关字段复制到自有类型:

class EchoImpl : public fidl::WireServer<fuchsia_examples::Echo> {
 public:
  void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {
    // Copy the contents of |request->value| (a fidl::StringView) to a string.
    std::string value_owned{request->value.get()};
    async::PostDelayedTask(
        dispatcher_,
        [value = value_owned, completer = completer.ToAsync()]() mutable {
          completer.Reply(fidl::StringView::FromExternal(value));
        },
        zx::duration(ZX_SEC(1)));
  }

  // ...
};

如需详细了解内存所有权,请参阅有线网域对象的内存所有权

运行示例

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

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

    fx set core.x64 --with //examples/fidl/cpp/server_async_completer:echo-cpp-async
    
  2. 构建 Fuchsia 映像:

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

    ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-cpp-async#meta/echo_realm.cm
    
  4. 启动 echo_client 实例:

    ffx component start /core/ffx-laboratory:echo_realm/echo_client
    

在客户端尝试连接到 Echo 协议时,服务器组件将启动。您应该会在设备日志 (ffx log) 中看到类似以下的输出。最左侧的列是时间戳:

[21611.962][echo_server][I] Running C++ echo server with natural types
[21611.965][echo_server][I] Incoming connection for fuchsia.examples.Echo
[21612.998][echo_client][I] (Natural types) got response: hello
[21613.999][echo_client][I] (Natural types) got response: hello
[21614.000][echo_client][I] (Natural types) got event: hello
[21615.002][echo_client][I] (Wire types) got response: hello
[21615.003][echo_client][I] (Natural types) got event: hello
[21615.003][echo_server][I] Client disconnected

请注意,Incoming connection for fuchsia.examples.Echo(Natural types) got response: hello 之间大约需要一秒钟的时间,因为服务器被编程为异步延迟一秒钟的响应。

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

ffx component destroy /core/ffx-laboratory:echo_realm