异步响应请求

前提条件

本教程以入门教程为基础。

概览

本教程的完整示例代码位于 //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 消息,同时将回复安排在未来一秒钟后进行。这意味着,如果客户端快速连续发送多个 EchoString 请求,我们的并发异步延迟任务数可能相同。

在代表线路网域对象的服务器中异步响应

当服务器读出有线网域对象时,您可以使用相同的 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 功能从服务器路由到客户端。

  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 之间大约需要 1 秒,因为服务器的编程为异步将响应延迟 1 秒。

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

ffx component destroy /core/ffx-laboratory:echo_realm