| # Implement an LLCPP 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 |
| |
| <!-- TODO(fxbug.dev/58758) <<../../common/server/overview.md>> --> |
| |
| 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" %} |
| ``` |
| |
| For more on FIDL methods and messaging models, refer to the [FIDL concepts][concepts] page. |
| |
| 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/llcpp/server/* |
| ``` |
| |
| ## Create the component {#component} |
| |
| To create a component: |
| |
| 1. Add a `main()` function to `examples/fidl/llcpp/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/llcpp/server/BUILD.gn`: |
| |
| ```gn |
| import("//build/components.gni") |
| |
| # 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_llcpp_server" |
| sources = [ "main.cc" ] |
| } |
| |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/BUILD.gn" region_tag="rest" %} |
| ``` |
| |
| <!-- TODO(fxbug.dev/58758) <<../../common/server/packages.md>> --> |
| |
| 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/llcpp/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/llcpp/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/llcpp/server:echo-llcpp-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:fuchsia.examples_llcpp"` to the `deps` of the `executable` |
| 2. Include the bindings into the main file with `#include <fidl/fuchsia.examples/cpp/wire.h>` |
| |
| The full `bin` target declaration should now look like this: |
| |
| ```gn |
| executable("bin") { |
| output_name = "fidl_echo_llcpp_server" |
| sources = [ "main.cc" ] |
| deps = [ "//examples/fidl/fuchsia.examples:fuchsia.examples_llcpp" ] |
| } |
| ``` |
| |
| ### 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/llcpp/server/main.cc" region_tag="impl" %} |
| ``` |
| |
| 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. |
| * It contains an optional `ServerBindingRef` in order to be able to send events |
| to the client. It gets set later in the class's `Bind()` function. |
| * The `Bind` method binds the implementation to a given `request`. |
| * The method for `EchoString` replies with the request value by using the |
| completer. |
| * The method for `SendString` uses the `binding_` member (if defined) to send |
| an `OnString` event containing the request value. |
| |
| You can verify that the implementation builds by running: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| ## Serve the protocol {#main} |
| |
| When running a component that implements a FIDL protocol, you must 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 our server implementation. |
| The resulting `fidl::ServerBindingRef` is reference to a server binding |
| that takes a FIDL protocol implementation and a channel, |
| and then listens on the channel for incoming requests. The binding then decodes |
| the requests, dispatches them to the correct method on our server class, and |
| writes 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 |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="main" highlight="2,3,4,5,32" %} |
| ``` |
| |
| The event loop is used to asynchronously listen for incoming connections and |
| requests from the client. This code initializes the loop, and obtains the |
| dispatcher, which will be used when binding the server implementation to a |
| channel. |
| |
| At the end of the main function, the code runs the loop to completion. |
| |
| ### Serve component's service directory |
| |
| The `svc::Outgoing` class serves the service directory ("/svc") for a given |
| component. This directory is where the outgoing FIDL protocols are installed so |
| that they can be provided to other components. The `ServeFromStartupInfo()` |
| function sets up the service directory with the startup handle. The startup |
| handle is a handle provided to every component by the system, so that they can |
| serve capabilities (e.g. FIDL protocols) to other components. |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="main" highlight="7,8,9,10,11,12,13,14,15,16,17" %} |
| ``` |
| |
| ### Serve the protocol {#server-handler} |
| |
| The server then registers the Echo protocol using `ougoing.svc_dir()->AddEntry()`. |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="main" highlight="17,18,19,20,21,22,23,24,25,26,27,28,29,30,31" %} |
| ``` |
| |
| The call to `AddEntry` installs a handler for the name of the FIDL protocol |
| (`fuchsia_examples::Echo::Name`, which is the string `"fuchsia.examples.Echo"`). |
| The handler will call the lambda function that we created, and this lambda |
| function will call `EchoImpl::Bind` with the |
| `fidl::ServerEnd<fuchsia_examples::Echo>`, which internally wraps a |
| `zx::channel`, that represents a request from a client. |
| |
| When a client requests access to `/svc/fuchsia.examples.Echo`, this function |
| will be called with a channel that represents the request. This channel is bound |
| to our server via the `Bind` function, and future requests from this client |
| will call. |
| |
| When the handler is called (i.e. when a client has requested to connect to the |
| `/svc/fuchsia.examples.Echo` protocol), it binds the incoming channel to our |
| `Echo` implementation, which will start listening for `Echo` requests on that |
| channel and dispatch them to the `EchoImpl` instance. `EchoImpl`'s call to |
| `fidl::BindServer` returns a `fidl::ServerBindingRef`, which is then stored so |
| the instance can be able to send events back to the client. |
| |
| ### 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/fdio"` and `"//zircon/system/ulib/svc"`: These are libraries used |
| to interact with the components environment (e.g. for serving protocols). |
| * `"//zircon/system/ulib/fidl"`: The LLCPP runtime, which contains utility code for |
| using the FIDL bindings, such as the `BindServer` function. |
| |
| The full `bin` target declaration should now look like this: |
| |
| ```gn |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/BUILD.gn" region_tag="bin" %} |
| ``` |
| |
| Import the dependencies by including them at the top of `examples/fidl/llcpp/server/main.cc`: |
| |
| ```cpp |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/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-llcpp-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/llcpp/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/llcpp/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#binding_to_a_component_and_sending_a_protocol_channel |
| [bindings-iface]: /docs/reference/fidl/bindings/llcpp-bindings.md#protocols |
| [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 |
| [concepts]: /docs/concepts/fidl/overview.md |