以非同步方式回應要求

必要條件

本教學課程的設計是以入門教學課程為基礎。

總覽

本教學課程的完整程式碼範例位於 //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. 設定建構作業,加入內含回音領域、伺服器和用戶端的提供套件:

    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 之間會相隔約一秒,因為伺服器的程式設計會使回應以非同步方式延遲一秒。

終止領域元件,停止執行並清理元件例項:

ffx component destroy /core/ffx-laboratory:echo_realm