blob: 2256ded1b73b1eebc5026a999f12c0131d069ce8 [file] [log] [blame] [view]
# Implement a C++ FIDL server
## Prerequisites
This tutorial builds on the [domain objects][domain-objects] tutorial. For the
full set of FIDL tutorials, refer to the [overview][overview].
## Overview
<!-- TODO(https://fxbug.dev/42136750) <<../../common/server/overview.md>> -->
This tutorial shows you how to implement a server for a FIDL protocol
(`fuchsia.examples/Echo`) and run it on Fuchsia. This protocol has one method
of each kind: a one-way 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 the FIDL protocol](#protocol).
* [Publish the protocol implementation](#serve).
* [Run the server](#run).
## Structure of the server example
The example code accompanying this tutorial is located in your Fuchsia checkout
at [`//examples/fidl/cpp/server`][cpp-server-src]. It consists of a server
component and its containing package. For more information about building
components, see [Build components][build-components].
To get the server component up and running, there are three targets that are
defined in `//examples/fidl/cpp/server/BUILD.gn`:
1. The raw executable file for the server. This produces a binary with the
specified output name that can run on Fuchsia:
```gn
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/BUILD.gn" region_tag="bin" %}
```
1. A component that is set up to run the server executable.
Components are the units of software execution on Fuchsia. A component is
described by its manifest file. In this case `meta/server.cml`
configures `echo-server` as an executable component which runs
`fidl_echo_cpp_server` in `:bin`.
```gn
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/BUILD.gn" region_tag="component" %}
```
The server component manifest is located at
`//examples/fidl/cpp/server/meta/server.cml`. The binary name in the
manifest must match the output name of the `executable` defined in
`BUILD.gn`.
```json5
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/meta/server.cml" region_tag="example_snippet" %}
```
1. 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.
```gn
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/BUILD.gn" region_tag="package" %}
```
### Building the server {#build}
You may build the server package via the following:
1. Add the server to your build configuration. This only needs to be done once:
```posix-terminal
fx set core.x64 --with //examples/fidl/cpp/server
```
1. Build the server package:
```posix-terminal
fx build //examples/fidl/cpp/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.
## Implement the FIDL protocol {#protocol}
`EchoImpl` implements the server request handler for the `fuchsia.examples/Echo`
protocol. To do so, `EchoImpl` inherits from the generated pure virtual server
interface `fidl::Server<fuchsia_examples::Echo>`, and overrides its pure virtual
methods corresponding to every one way and two way call:
```cpp
class EchoImpl : public fidl::Server<fuchsia_examples::Echo> {
public:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/main.cc" region_tag="impl" %}
// ... other methods from examples/fidl/cpp/server/main.cc omitted, to be covered later.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/main.cc" region_tag="binding_ref" %}
};
```
Note: in this tutorial the `EchoString` handler replies synchronously. For
asynchronous replies, see
[responding to requests asynchronously][responding-asynchronously].
### Bind the implementation to a server endpoint
Implementing the request handlers is only half the story. The server needs to be
able to monitor new messages that arrives on a
[server endpoint][server-endpoint]. To do this, `EchoImpl` defines two more
methods: a `BindSelfManagedServer` static factory function that creates a
new `EchoImpl` instance to handle requests on a new server endpoint
`fidl::ServerEnd<fuchsia_examples::Echo>`, and an `OnUnbound` method that
is called when the connection is torn down:
```cpp
/* Inside `class EchoImpl {`... */
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/main.cc" region_tag="bind_server" %}
```
## Publish the protocol implementation {#server}
A component that implements a FIDL protocol can expose that FIDL
protocol to other components. This is done by publishing the protocol
implementation to the component's
[outgoing directory][glossary.outgoing-directory]. This complete process is
described in further detail in the [Life of a protocol open][protocol-open].
We can use `component::OutgoingDirectory` from the C++ component runtime library
to perform the heavy lifting.
To depend on the component runtime library:
```gn
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/BUILD.gn" region_tag="bin" highlight="12" %}
```
Import the library at the top of `examples/fidl/cpp/server/main.cc`:
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/main.cc" region_tag="includes" highlight="3" %}
```
Serve the component's outgoing directory:
```cpp
int main(int argc, const char** argv) {
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/main.cc" region_tag="serve-out-dir" %}
// ...
```
### Serve the protocol {#server-handler}
The server then registers the Echo protocol using `outgoing.AddProtocol`.
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/main.cc" region_tag="main" highlight="27,28,29,30,31,32,33,34,35,36" %}
```
The call to `AddProtocol` installs a handler at the name of the FIDL protocol
(`fidl::DiscoverableProtocolName<fuchsia_examples::Echo>`, which is the string
`"fuchsia.examples.Echo"`). When a client component connects to
`fuchsia.examples.Echo`, `outgoing` will call the lambda function that we
created with a server endpoint corresponding to the client endpoint from the
client, and this lambda function will call the `EchoImpl::BindSelfManagedServer`
detailed above to bind the server endpoint to a new instance of `EchoImpl`.
Our main method will keep listening for incoming requests on the
[async loop][async-loop].
## Test the server {#run}
After [building the server](#build), you may run the example on a running
instance of Fuchsia emulator via
```posix-terminal
ffx component run /core/ffx-laboratory:echo-server fuchsia-pkg://fuchsia.com/echo-cpp-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 C++ echo server with natural types
```
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]
## Serve requests using wire domain objects {#using-wire}
The above tutorial implements a server with
[natural domain objects][natural-types]: the server receives requests
represented in natural domain objects, and sends replies encoded from natural
domain objects. When optimizing for performance and heap allocation, one may
implement a server that speaks [wire domain objects][wire-types], i.e. a wire
server. Here is the `EchoImpl` rewritten to use wire domain objects:
```cpp
class EchoImpl final : public fidl::WireServer<fuchsia_examples::Echo> {
public:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/wire/main.cc" region_tag="handlers" %}
// ... |BindSelfManagedServer| etc omitted. Those stay the same.
};
```
The relevant classes and functions used in a wire server have similar shapes to
those used in a natural server. When a different class or function is called
for, the wire counterpart is usually prefixed with `Wire`. There are also
small differences in pointers vs references and argument structure:
* The server interface implemented by a natural server is
`fidl::Server<fuchsia_examples::Echo>`. The server interface implemented by a
wire server is `fidl::WireServer<fuchsia_examples::Echo>`.
* The handler function in a natural server takes a reference to the request
message. The `Reply` method takes a single argument that is the response
payload domain object:
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/main.cc" region_tag="impl-echo-string" adjust_indentation="auto" %}
```
Whereas the handler function in a wire server takes a view (akin to a pointer)
of the request message. When the response payload is a struct, the `Reply`
method flattens the list of struct fields in the response payload into
separate arguments (here, a single `fidl::StringView` argument):
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/server/wire/main.cc" region_tag="impl-echo-string" adjust_indentation="auto" %}
```
* The function to send events with natural types is `fidl::SendEvent`. The
function to send events with wire types is `fidl::WireSendEvent`. Struct
fields are also flattened into separate arguments when sending an event.
The same `fidl::BindServer` function may be used to bind either a natural server
or a wire server.
The full example code for a wire server is located in your Fuchsia checkout
at [`//examples/fidl/cpp/server/wire`][cpp-wire-server-src].
<!-- 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
[glossary.outgoing-directory]: /docs/glossary/README.md#outgoing-directory
[cpp-server-src]: /examples/fidl/cpp/server
[cpp-wire-server-src]: /examples/fidl/cpp/server/wire
[domain-objects]: /docs/development/languages/fidl/tutorials/cpp/basics/domain-objects.md
[build-components]: /docs/development/components/build.md
[products]: /docs/development/build/build_system/boards_and_products.md
[protocol-open]: /docs/concepts/components/v2/capabilities/life_of_a_protocol_open.md#binding_to_a_component_and_sending_a_protocol_channel
[compiling-fidl]: /docs/development/languages/fidl/tutorials/fidl.md
[async-loop]: /sdk/lib/async-loop/include/lib/async-loop/cpp/loop.h
[overview]: /docs/development/languages/fidl/tutorials/overview.md
[concepts]: /docs/concepts/fidl/overview.md
[responding-asynchronously]: /docs/development/languages/fidl/tutorials/cpp/topics/async-completer.md
[server-endpoint]: /docs/reference/fidl/language/language.md#protocols-use
[natural-types]: /docs/development/languages/fidl/tutorials/cpp/basics/domain-objects.md#using-natural
[wire-types]: /docs/development/languages/fidl/tutorials/cpp/basics/domain-objects.md#using-wire