Google is committed to advancing racial equity for Black communities. See how.

Handling multiple clients

Prerequisites

This tutorial builds on the HLCPP getting started tutorials.

Overview

This tutorial updates the Echo client from the getting started tutorials to make multiple connections to the server, and update the Echo server to handle multiple client connections. For running multiple instances of a server (or multiple FIDL protocols), see the tutorial on services.

The full example code for this tutorial is located at: //examples/fidl/hlcpp/multiple_clients

Implement the server

In the previous implementation, the main() function initialized a single fidl::Binding, and bound any incoming requests to it:

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();
}

This means that if a second client tries to connect to the server at the same time, the second call to binding.Bind will overwrite the channel from the first client. To support multiple clients, keep track of multiple fidl::Bindings (one for each client) using a fidl::BindingSet:

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();
}

Using a binding set also simplifies the code, since it no longer need to create a custom handler. The binding set has a GetHandler method which returns a handler that creates a new Binding and stores it in a vector.

To use fidl::BindingSet, include lib/fidl/cpp/binding_set.h.

Implement the client

In order to manage multiple clients connected to a protocol, the FIDL HLCPP runtime library provides an anolog to fidl::BindingSet: the fidl::InterfacePtrSet. Use the class to write code that makes multiple connections to the same protocol:

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();
      }
    });
  }

  return responses == kNumClients ? 0 : 1;
}

The code for setting up a proxy and making requests is the same as in the [client tutorial][client-tut-main] except it uses an interface pointer set to simplify the process of broadcasting a message to a set of clients. An added benefit of using fidl::InterfacePtrSet and fidl::BindingSet is that any binding or interface pointer that experiences an error on its channel is automatically removed from the set.

To use fidl::InterfacePtrSet, include lib/fidl/cpp/interface_ptr_set.h.

Run the example

  1. Configure the GN build as follows:

    fx set core.x64 --with //examples/fidl/hlcpp/multiple_clients/client --with //examples/fidl/hlcpp/multiple_clients/server --with //examples/fidl/test:echo-launcher

  2. Build

    fx build

  3. Run launcher by passing it the client URL, the server URL, and the protocol that the server provides to the client:

    fx shell run fuchsia-pkg://fuchsia.com/echo-launcher#meta/launcher.cmx fuchsia-pkg://fuchsia.com/echo-hlcpp-multi-client#meta/echo-client.cmx fuchsia-pkg://fuchsia.com/echo-hlcpp-multi-server#meta/echo-server.cmx fuchsia.examples.Echo