| # Implement a FIDL server |
| |
| ## Prerequisites |
| |
| This tutorial builds on the [Compiling FIDL][fidl-intro] tutorial. For the |
| full set of FIDL tutorials, refer to the [overview][overview]. |
| |
| ## Overview |
| |
| This tutorial shows you how to implement a FIDL protocol |
| (`fuchsia.examples.Echo`) and run it on Fuchsia. This protocol has one method |
| of each kind: a fire and forget method, a two-way method, and an event: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/echo.test.fidl" region_tag="echo" %} |
| ``` |
| |
| This document covers how to complete the following tasks: |
| |
| * Implement a FIDL protocol. |
| * Build and run a package on Fuchsia. |
| * Serve a FIDL protocol. |
| |
| The tutorial starts by creating a component that is served to a Fuchsia device |
| and run. Then, it gradually adds functionality to get the server up and running. |
| |
| If you want to write the code yourself, delete the following directories: |
| |
| ```posix-terminal |
| rm -r examples/fidl/hlcpp/server/* |
| ``` |
| |
| ## Create the component {#component} |
| |
| To create a component: |
| |
| 1. Add a `main()` function to `examples/fidl/hlcpp/server/main.cc`: |
| |
| ```cpp |
| #include <stdio.h> |
| |
| int main(int argc, const char** argv) { |
| printf("Hello, world!\n"); |
| return 0; |
| } |
| ``` |
| |
| 1. Declare a target for the server in `examples/fidl/hlcpp/server/BUILD.gn`: |
| |
| ```gn |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/BUILD.gn" region_tag="imports" %} |
| |
| # Declare an executable for the server. This produces a binary with the |
| # specified output name that can run on Fuchsia. |
| executable("bin") { |
| output_name = "fidl_echo_hlcpp_server" |
| sources = [ "main.cc" ] |
| } |
| |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/BUILD.gn" region_tag="rest" %} |
| ``` |
| |
| To get the server component up and running, there are three targets that are |
| defined: |
| |
| * The raw executable file for the server that is built to run on Fuchsia. |
| * A component that is set up to simply run the server executable, |
| which is described using the component's manifest file. |
| * The component is then put into a package, which is the unit of software |
| distribution on Fuchsia. In this case, the package just contains a |
| single component. |
| |
| For more details on packages, components, and how to build them, refer to |
| the [Building components][building-components] page. |
| |
| 1. Add a component manifest in `examples/fidl/hlcpp/server/meta/server.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/server/meta/server.cml" region_tag="example_snippet" %} |
| ``` |
| |
| <!-- TODO(fxbug.dev/58758) <<../../common/server/qemu.md>> --> |
| |
| 1. Add the server to your build configuration: |
| |
| ```posix-terminal |
| fx set core.qemu-x64 --with //examples/fidl/hlcpp/server:echo-hlcpp-server |
| ``` |
| |
| Note: This build configuration assumes your device target is the emulator. |
| To run the example on a physical device, select the appropriate |
| [product configuration][products] for your hardware. |
| |
| 1. Build the Fuchsia image: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| ## Implement the server |
| |
| ### Add a dependency on the FIDL library |
| |
| 1. Add `"//examples/fidl/fuchsia.examples"` to the `deps` of the `executable` |
| 2. Include the bindings into the main file with `#include <fuchsia/examples/cpp/fidl.h>` |
| |
| The full `bin` target declaration should now look like this: |
| |
| ```gn |
| executable("bin") { |
| output_name = "fidl_echo_hlcpp_server" |
| sources = [ "main.cc" ] |
| deps = [ "//examples/fidl/fuchsia.examples" ] |
| } |
| ``` |
| |
| ### Add an implementation for the protocol {#impl} |
| |
| Add the following to `main.cc`, above the `main()` function: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/main.cc" region_tag="server" %} |
| ``` |
| |
| The implementation contains the following elements: |
| |
| * The class subclasses the [generated protocol class][bindings-iface] and |
| overrides its pure virtual methods corresponding to the protocol methods. |
| * The method for `EchoString` replies with the request value by calling the |
| callback on it. |
| * The method for `SendString` does not take a callback since this method does |
| not have a response. Instead, the implementation sends an `OnString` event |
| using an `Echo_EventSender`. |
| * The class contains a pointer to an `Echo_EventSender`. This will be set |
| later in the `main()` function. |
| |
| You can verify that the implementation is correct by running: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| ## Serve the protocol {#main} |
| |
| To run a component that implements a FIDL protocol, make a request to the |
| [component manager][component-manager] to expose that FIDL protocol to other |
| components. The component manager then routes any requests for the echo protocol |
| to our server. |
| |
| To fulfill these requests, the component manager requires the name of the protocol |
| as well as a handler that it should call when it has any incoming requests to |
| connect to a protocol matching the specified name. |
| |
| The handler passed to it is a function that takes a channel (whose remote |
| end is owned by the client), and binds it to a `fidl::Binding` that has been |
| initialized using the server implementation. The `fidl::Binding` is a class |
| from the FIDL runtime that takes a FIDL protocol implementation and a channel, |
| and then listens on the channel for incoming requests. It will then decode |
| the requests, dispatch them to the correct method on our server class, and |
| write any response back to the client. Our main method will keep listening |
| for incoming requests on an [async loop][async-loop]. |
| |
| This complete process is described in further detail in the |
| [Life of a protocol open][protocol-open]. |
| |
| ### Initialize the event loop |
| |
| The first aspect is the use of an async loop: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/main.cc" region_tag="main" highlight="2,15" %} |
| ``` |
| |
| The code first initializes the loop and registers it as the default dispatcher |
| for the current thread. This comes first, as the async code in the `main()` |
| function will register itself with the default dispatcher, which is a static |
| thread local variable (which is why it does not need to be passed explicitly in |
| the rest of the code). At the end of the main function, the code runs the async loop. |
| |
| ### Initialize the binding |
| |
| Then, the code initializes the `fidl::Binding` as mentioned above: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/main.cc" region_tag="main" highlight="4,5,6" %} |
| ``` |
| |
| In order to run, a binding needs two things: |
| |
| * An implementation of a protocol. |
| * A channel that the binding will listen for messages for that protocol on. |
| The binding is first initialized using the echo implementation, and will be |
| bound to a channel later. |
| |
| The code also sets the event sender that is used to send events to the client. |
| The event sender is obtained using the `events()` method on the `Binding`, and then passed to |
| the `EchoImpl` class. |
| |
| ### Define a protocol request handler {#handler} |
| |
| Next, the code defines a handler for incoming requests from a client: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/main.cc" region_tag="main" highlight="7,8,9,10" %} |
| ``` |
| |
| * An "incoming request" is not a request for a specific method of `Echo` |
| protocol, but rather a general request from a client to connect to an |
| implementation of the `Echo` protocol. |
| * The request is defined as a `fidl::InterfaceRequest<Echo>`. This is a |
| type-safe wrapper around a channel that indicates two things: |
| * `InterfaceRequest` indicates that this is the server end of a channel (i.e. |
| a client is connected to the remote end of the channel) |
| * The template parameter `Echo` means that the client expects that a server |
| implementing the `Echo` protocol binds itself to this channel. The client |
| analog of this (i.e. the type that is being used on the client side to |
| represent the other end of this channel) is a `fidl::InterfaceHandle<Echo>`. |
| * The handler simply takes the channel sent from the client, and binds it to the |
| `Echo` binding. |
| * Once this happens, the `Binding` starts handling messages on the channel |
| according to the `Echo` protocol. This is an example of [protocol request |
| pipelining][pipeline], which is explored in a [later tutorial][pipeline-tut]. |
| |
| ### Register the protocol request handler |
| |
| Finally, the code registers the handler with the component manager: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/main.cc" region_tag="main" highlight="11,12" %} |
| ``` |
| |
| The first line initializes and serves the outgoing directory, which contains |
| protocols that this component exposes to other components, and the second line |
| adds the handler to the outgoing directory. |
| |
| An implicit second parameter besides the handler is the name that this handler |
| should be registered to. By default, this parameter is the name of the protocol |
| being passed in, which is generated because of the presence [`[Discoverable]` |
| attribute][discoverable] on the `Echo` protocol. In other words, after executing |
| this line you should be able to call `ls` on the component's `/out` directory |
| and see an entry called `fuchsia.examples.Echo`. |
| |
| ### Add new dependencies {#deps} |
| |
| This new code requires the following additional dependencies: |
| |
| * `"//zircon/system/ulib/async-loop:async-loop-cpp"` and `"//zircon/system/ulib/async-loop:async-loop-default"`, which contain the async loop code. |
| * `"//sdk/lib/sys/cpp"`: The component framework C++ runtime, which contains |
| utility code for interacting with the component's environment. |
| * `"//sdk/lib/fidl/cpp"`: The FIDL C++ runtime, which contains utility code for |
| using the FIDL bindings. |
| |
| The full `bin` target declaration should now look like this: |
| |
| ```gn |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/BUILD.gn" region_tag="bin" %} |
| ``` |
| |
| Import the dependencies by including them at the top of `examples/fidl/hlcpp/server/main.cc`: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/server/main.cc" region_tag="includes" %} |
| ``` |
| |
| ## Test the server |
| |
| Rebuild: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| Then run the server component: |
| |
| ```posix-terminal |
| ffx component run fuchsia-pkg://fuchsia.com/echo-hlcpp-server#meta/echo_server.cm |
| ``` |
| |
| Note: Components are resolved using their [component URL][glossary.component-url], |
| which is determined with the [`fuchsia-pkg://`][glossary.fuchsia-pkg-url] scheme. |
| |
| You should see output similar to the following in the device logs |
| (`ffx log`): |
| |
| ```none {:.devsite-disable-click-to-copy} |
| [ffx-laboratory:echo_server][][I] Running echo server |
| ``` |
| |
| The server is now running and waiting for incoming requests. |
| The next step will be to write a client that sends `Echo` protocol requests. |
| For now, you can simply terminate the server component: |
| |
| ```posix-terminal |
| ffx component destroy /core/ffx-laboratory:echo_server |
| ``` |
| |
| Note: Component instances are referenced by their |
| [component moniker][glossary.moniker], which is determined by their location in |
| the [component instance tree][glossary.component-instance-tree] |
| |
| <!-- xrefs --> |
| [glossary.component-instance-tree]: /docs/glossary/README.md#component-instance-tree |
| [glossary.component-url]: /docs/glossary/README.md#component-url |
| [glossary.fuchsia-pkg-url]: /docs/glossary/README.md#fuchsia-pkg-url |
| [glossary.moniker]: /docs/glossary/README.md#moniker |
| [fidl-intro]: /docs/development/languages/fidl/tutorials/hlcpp/basics/using-fidl.md |
| [building-components]: /docs/development/components/build.md |
| [products]: /docs/concepts/build_system/boards_and_products.md |
| [declaring-fidl]: /docs/development/languages/fidl/tutorials/fidl.md |
| [depending-fidl]: /docs/development/languages/fidl/tutorials/hlcpp/basics/using-fidl.md |
| [component-manager]: /docs/concepts/components/v2/component_manager.md |
| [protocol-open]: /docs/concepts/components/v2/capabilities/life_of_a_protocol_open.md |
| [discoverable]: /docs/reference/fidl/bindings/hlcpp-bindings.md#discoverable |
| [bindings-iface]: /docs/reference/fidl/bindings/hlcpp-bindings.md#protocols |
| [pipeline]: /docs/development/api/fidl.md#request-pipelining |
| [pipeline-tut]: /docs/development/languages/fidl/tutorials/hlcpp/topics/request-pipelining.md |
| [compiling-fidl]: /docs/development/languages/fidl/tutorials/fidl.md |
| [async-loop]: /zircon/system/ulib/async-loop/include/lib/async-loop/cpp/loop.h |
| [overview]: /docs/development/languages/fidl/tutorials/overview.md |