This tutorial builds on the Compiling FIDL tutorial. For the full set of FIDL tutorials, refer to the 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:
{% 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 page.
This document covers how to complete the following tasks:
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:
rm -r examples/fidl/llcpp/server/*
To create a component:
Add a main()
function to examples/fidl/llcpp/server/main.cc
:
int main(int argc, const char** argv) { return 0; }
Declare a target for the server in examples/fidl/llcpp/server/BUILD.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" %}
To get the server component up and running, there are three targets that are defined:
For more details on packages, components, and how to build them, refer to the Building components page.
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.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/meta/server.cml" region_tag="example_snippet" %}
Add the server to your build configuration:
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 for your hardware.
Build the Fuchsia image:
fx build
Add the fuchsia.examples
FIDL library target as a dependency of your executable
in examples/fidl/llcpp/server/BUILD.gn
:
executable("bin") { output_name = "fidl_echo_llcpp_server" sources = [ "main.cc" ] {{ '<strong>' }}deps = [ "//examples/fidl/fuchsia.examples:fuchsia.examples_llcpp" ]{{ '</strong>' }} }
Import the LLCPP bindings at the top of examples/fidl/llcpp/server/main.cc
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="fidl_includes" %}
Add the following to main.cc
, above the main()
function:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="impl" %}
The implementation contains the following elements:
ServerBindingRef
in order to be able to send events to the client.request
.EchoString
replies synchronously with the request value by using the completer (for asynchronous replies, see responding to requests asynchronously in LLCPP).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:
fx build
When running a component that implements a FIDL protocol, you must make a request to the 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.
This complete process is described in further detail in the Life of a protocol open.
This new code requires the following additional dependencies:
"//zircon/system/ulib/async-loop:async-loop-cpp"
: This library contains the asynchronous event loop code."//sdk/lib/sys/component/cpp"
: This library is used to publish capabilities, e.g. protocols, to the component's outgoing directory."//sdk/lib/syslog/cpp"
: This library is used to log messages.Add the library targets as dependencies of your executable
in examples/fidl/llcpp/server/BUILD.gn
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/BUILD.gn" region_tag="bin" highlight="6,7,8" %}
Import these dependencies at the top of examples/fidl/llcpp/server/main.cc
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="includes" %}
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="main" highlight="2,3,4,5,30" %}
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.
The component::OutgoingDirectory
class serves the outgoing directory 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 outgoing 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.
{% 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" %}
The server then registers the Echo protocol using outgoing.AddProtocol
.
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/server/main.cc" region_tag="main" highlight="16,17,18,19,20,21,22,23,24,25,26,27" %}
The call to AddProtocol
installs a handler for the name of the FIDL protocol (fidl::DiscoverableProtocolName<fuchsia_examples::Echo>
, which is the string "fuchsia.examples.Echo"
). The handler will call the lambda function that we created, and this lambda function will construct an EchoImpl
with the fidl::ServerEnd<fuchsia_examples::Echo>
, which internally wraps a zx::channel
, that represents a request from a client. The EchoImpl
stays alive the connection is torn down, at which point it deletes itself.
When the handler is called (i.e. when a client has requested to connect to /svc/fuchsia.examples.Echo
), 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 constructor populates a fidl::ServerBindingRef
which is used to send events back to the client.
Rebuild:
fx build
Then run the server component:
ffx component run fuchsia-pkg://fuchsia.com/echo-llcpp-server#meta/echo_server.cm
Note: Components are resolved using their component URL, which is determined with the fuchsia-pkg://
scheme.
You should see output similar to the following in the device logs (ffx log
):
[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:
ffx component destroy /core/ffx-laboratory:echo_server
Note: Component instances are referenced by their component moniker, which is determined by their location in the component instance tree