| # Implement a FIDL client |
| |
| ## Prerequisites |
| |
| This tutorial builds on the [FIDL server][server-tut] tutorial. For the |
| full set of FIDL tutorials, refer to the [overview][overview]. |
| |
| ## Overview |
| |
| This tutorial implements a client for a FIDL protocol and runs it against the |
| server created in the [previous tutorial][server-tut]. The client in this |
| tutorial is asynchronous. There is an [alternate tutorial][sync-client] for |
| synchronous clients. |
| |
| If you want to write the code yourself, delete the following directories: |
| |
| ```posix-terminal |
| rm -r examples/fidl/hlcpp/client/* |
| ``` |
| |
| ## Create the component |
| |
| Create a new component project at `examples/fidl/hlcpp/client`: |
| |
| 1. Add a `main()` function to `examples/fidl/hlcpp/client/main.cc`: |
| |
| ```cpp |
| int main(int argc, const char** argv) { |
| printf("Hello, world!\n"); |
| return 0; |
| } |
| ``` |
| |
| 1. Declare a target for the client in `examples/fidl/hlcpp/client/BUILD.gn`: |
| |
| ```gn |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/BUILD.gn" region_tag="imports" %} |
| |
| # Declare an executable for the client. |
| executable("bin") { |
| output_name = "fidl_echo_hlcpp_client" |
| sources = [ "main.cc" ] |
| } |
| |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/BUILD.gn" region_tag="rest" %} |
| ``` |
| |
| 1. Add a component manifest in `examples/fidl/hlcpp/client/meta/client.cml`: |
| |
| Note: The binary name in the manifest must match the output name of the |
| `executable` defined in the previous step. |
| |
| ```json5 |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/meta/client.cml" region_tag="example_snippet" %} |
| ``` |
| |
| 1. Once you have created your component, ensure that you can add it to the |
| build configuration: |
| |
| ```posix-terminal |
| fx set core.x64 --with //examples/fidl/hlcpp/client:echo-client |
| ``` |
| |
| 1. Build the Fuchsia image: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| ## Edit GN dependencies |
| |
| 1. Add the following dependencies: |
| |
| ```gn |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/BUILD.gn" region_tag="deps" %} |
| ``` |
| |
| 1. Then, include them in `main.cc`: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/main.cc" region_tag="includes" %} |
| ``` |
| |
| The reason for including these dependencies is explained in the |
| [server tutorial][server-tut-deps]. |
| |
| ## Connect to the server {#main} |
| |
| The steps in this section explain how to add code to the `main()` function |
| that connects the client to the server and makes requests to it. |
| |
| ### Initialize the event loop |
| |
| As in the server, the code first sets up an async loop so that the client can |
| listen for incoming responses from the server without blocking. |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/main.cc" region_tag="main" highlight="2,28" %} |
| ``` |
| |
| ### Initialize a proxy class {#proxy} |
| |
| In the context of FIDL, proxy designates the code |
| generated by the FIDL bindings that enables users to make |
| remote procedure calls to the server. In HLCPP, the proxy takes the form |
| of a class with methods corresponding to each FIDL protocol method. |
| |
| The code then creates a proxy class for the `Echo` protocol, and connects it |
| to the server. |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/main.cc" region_tag="main" highlight="4,5,6" %} |
| ``` |
| |
| * [`fuchsia::examples::EchoPtr`][proxy] is an alias for |
| `fidl::InterfaceRequest<fuchsia::examples::Echo>` generated by the bindings. |
| * Analogous to the `fidl::Binding<fuchsia::examples::Echo>` used in the server, |
| `fidl::InterfaceRequest<fuchsia::examples::Echo>` is parameterized by a FIDL |
| protocol and a channel it will proxy requests over the channel, and listen for |
| incoming responses and events. |
| * The code calls `EchoPtr::NewRequest()`, which creates a channel, |
| binds the class to one end of the channel, and returns the other end of the |
| channel. |
| * The returned end of the channel is passed to `sys::ServiceDirectory::Connect()`. |
| * Analogous to the call to `context->out()->AddPublicService()` on the server |
| side, `Connect` has an implicit second parameter here, which is the protocol |
| name (`"fuchsia.examples.Echo"`). This is where the input to the handler |
| defined in the [previous tutorial][server-tut-handler] comes from: the |
| client passes it in to `Connect`, which then passes it to the handler. |
| |
| An important point to note here is that this code assumes that `/svc` already |
| contains an instance of the `Echo` protocol. This is not the case by default |
| because of the sandboxing provided by the component framework. |
| |
| ### Set an error handler |
| |
| Finally, the code sets an error handler for the proxy: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/main.cc" region_tag="main" highlight="8,9,10" %} |
| ``` |
| |
| ### Send requests to the server |
| |
| The code makes two requests to the server: |
| |
| * An `EchoString` request |
| * A `SendString` request |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/main.cc" region_tag="main" highlight="14,15,16,17,18,19,20" %} |
| ``` |
| |
| For `EchoString` the code passes in a callback to handle the response. |
| `SendString` does not require such a callback because the method does not |
| have any response. |
| |
| ### Set an event handler |
| |
| The code then sets a handler for any incoming `OnString` events: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/main.cc" region_tag="main" highlight="21,22,23,24,25,26" %} |
| ``` |
| |
| ### Terminate the event loop |
| |
| The code waits to receive both a response to the `EchoString` method as well as an |
| `OnString` event (which in the [current implementation][server-tut-impl] is sent after receiving a |
| `SendString` request) before quitting from the loop. The code returns a successful exit |
| code only if it receives both a response and an event: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/client/main.cc" region_tag="main" highlight="13,17,18,23,24,29" %} |
| ``` |
| |
| ## Run the client |
| |
| In order for the client and server to communicate using the `Echo` protocol, |
| component framework must route the `fuchsia.examples.Echo` capability from the |
| server to the client. For this tutorial, a [realm][glossary.realm] component is |
| provided to declare the appropriate capabilities and routes. |
| |
| Note: You can explore the full source for the realm component at |
| [`//examples/fidl/echo-realm`](/examples/fidl/echo-realm) |
| |
| 1. Configure your build to include the provided package that includes the |
| echo realm, server, and client: |
| |
| ```posix-terminal |
| fx set core.x64 --with //examples/fidl/hlcpp:echo-hlcpp-client |
| ``` |
| |
| 1. Build the Fuchsia image: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| 1. Run the `echo_realm` component. This creates the client and server component |
| instances and routes the capabilities: |
| |
| ```posix-terminal |
| ffx component run /core/ffx-laboratory:echo_realm fuchsia-pkg://fuchsia.com/echo-hlcpp-client#meta/echo_realm.cm |
| ``` |
| |
| 1. Start the `echo_client` instance: |
| |
| ```posix-terminal |
| ffx component start /core/ffx-laboratory:echo_realm/echo_client |
| ``` |
| |
| The server component starts when the client attempts to connect to the `Echo` |
| protocol. You should see output similar to the following in the device logs |
| (`ffx log`): |
| |
| ```none {:.devsite-disable-click-to-copy} |
| [echo_server][][I] Running echo server |
| [echo_client][][I] Got event hi |
| [echo_client][][I] Got response hello |
| ``` |
| |
| Terminate the realm component to stop execution and clean up the component |
| instances: |
| |
| ```posix-terminal |
| ffx component destroy /core/ffx-laboratory:echo_realm |
| ``` |
| |
| <!-- xrefs --> |
| [glossary.realm]: /docs/glossary/README.md#realm |
| [server-tut]: /docs/development/languages/fidl/tutorials/hlcpp/basics/server.md |
| [server-tut-component]: /docs/development/languages/fidl/tutorials/hlcpp/basics/server.md#component |
| [server-tut-impl]: /docs/development/languages/fidl/tutorials/hlcpp/basics/server.md#impl |
| [server-tut-deps]: /docs/development/languages/fidl/tutorials/hlcpp/basics/server.md#dependencies |
| [server-tut-handler]: /docs/development/languages/fidl/tutorials/hlcpp/basics/server.md#handler |
| [sync-client]: /docs/development/languages/fidl/tutorials/hlcpp/basics/sync_client.md |
| [proxy]: /docs/reference/fidl/bindings/hlcpp-bindings.md#protocols-client |
| [overview]: /docs/development/languages/fidl/tutorials/overview.md |
| [environment]: /docs/concepts/components/v2/environments.md |