處理多個用戶端

必要條件

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

總覽

本教學課程會更新入門教學課程中的Echo 用戶端,以便建立多個與伺服器的連線,並更新Echo 伺服器,以便處理多個用戶端連線。如要執行多個伺服器執行個體 (或多個 FIDL 通訊協定),請參閱 服務教學課程。

本教學課程的完整範例程式碼位於 //examples/fidl/hlcpp/multiple_clients

實作伺服器

先前的實作中,main() 函式會初始化單一 fidl::Binding,並將任何傳入的要求繫結至該函式:

int main(int argc, const char** argv) {
  async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);

  EchoImpl impl;
  fidl::Binding<fuchsia::examples::Echo> binding(&impl);
  impl.event_sender_ = &binding.events();
  fidl::InterfaceRequestHandler<fuchsia::examples::Echo> handler =
      [&](fidl::InterfaceRequest<fuchsia::examples::Echo> request) {
        binding.Bind(std::move(request));
      };
  auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
  context->outgoing()->AddPublicService(std::move(handler));

  printf("Running echo server\n");
  return loop.Run();
}

也就是說,如果第二個用戶端在同一時間嘗試連線至伺服器,第二次呼叫 binding.Bind 會覆寫第一個用戶端的管道。如要支援多個用戶端,請使用 fidl::BindingSet 追蹤多個 fidl::Binding (每個用戶端一個):

int main(int argc, const char** argv) {
  async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);

  EchoImpl impl;
  fidl::BindingSet<fuchsia::examples::Echo> bindings;
  auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
  context->outgoing()->AddPublicService(bindings.GetHandler(&impl));

  printf("Running echo server\n");
  return loop.Run();
}

繫結集也簡化了程式碼,因為不再需要建立自訂處理常式。繫結組合含有 GetHandler 方法,此方法會傳回可建立新的 Binding 並儲存至向量的處理常式。

如要使用 fidl::BindingSet,請加入 lib/fidl/cpp/binding_set.h

實作用戶端

為了管理與通訊協定連線的多個用戶端,FIDL HLCPP 執行階段程式庫提供了 fidl::BindingSet 的類似項目:fidl::InterfacePtrSet。使用這個類別來編寫程式碼,以便與相同通訊協定建立多個連線:

int main(int argc, const char** argv) {
  async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);

  auto context = sys::ComponentContext::Create();

  fidl::InterfacePtrSet<fuchsia::examples::Echo> echoers;
  for (int i = 0; i < kNumClients; i++) {
    fuchsia::examples::EchoPtr proxy;
    context->svc()->Connect(proxy.NewRequest());
    proxy.set_error_handler([&loop](zx_status_t status) {
      std::cout << "Error reading incoming message: " << status << std::endl;
      loop.Quit();
    });
    echoers.AddInterfacePtr(std::move(proxy));
  }

  size_t responses = 0;
  for (auto& echoer : echoers.ptrs()) {
    (*echoer)->EchoString("Hello echoer " + std::to_string(responses++), [&](std::string response) {
      std::cout << "Got response " << response << std::endl;
      if (responses == echoers.size()) {
        loop.Quit();
      }
    });
  }

  loop.Run();
  return responses == kNumClients ? 0 : 1;
}

設定 Proxy 及發出要求的程式碼與用戶端教學課程中的相同,差別只在於會使用介面指標集,簡化向一組用戶端廣播訊息的程序。使用 fidl::InterfacePtrSetfidl::BindingSet 的額外好處是,如果綁定或介面指標在其管道上發生錯誤,系統會自動將其從集合中移除。

如要使用「fidl::InterfacePtrSet」,請加入 lib/fidl/cpp/interface_ptr_set.h

執行範例

為了讓用戶端和伺服器使用 Echo 通訊協定進行通訊,元件架構必須將 fuchsia.examples.Echo 能力從伺服器路由至用戶端。

中探索領域元件的完整來源。
  1. 設定建構作業,加入內含回音領域、伺服器和用戶端的提供套件:

    fx set core.x64 --with //examples/fidl/hlcpp:echo-hlcpp-multi-client
    
  2. 建構 Fuchsia 映像檔:

    fx build
    
  3. 執行 echo_realm 元件。這會建立用戶端和伺服器元件執行個體,並將功能導向:

    ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-hlcpp-multi-client#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 echoer 0

終止領域元件以停止執行並清除元件執行個體:

ffx component destroy /core/ffx-laboratory:echo_realm