This tutorial builds on the Dart FIDL packages 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/dart/server/*
To create a component:
Add a main()
function to examples/fidl/dart/server/lib/main.dart
:
void main(List<String> args) { print("Hello, world!"); }
Declare a target for the server in examples/fidl/dart/server/BUILD.gn
:
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/server/BUILD.gn" %}
The dart_app
template defines multiple parts:
path
refers to the location of the file in the tree, and dest
refers to the target location of the manifest within the component.For more details on packages, components, and how to build them, refer to Building components.
The dependencies will used later when implementing the FIDL server, and are not needed yet at this step.
Add a component manifest in examples/fidl/rust/server/server.cmx
:
Note: The binary name in the manifest must match the name of the dart_app
, which is used to define the Dart executable.
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/server/meta/server.cmx" %}
Note: The instructions in this section are geared towards running the component on QEMU, as this is the simplest way to get started with running Fuchsia, but it is also possible to pick a different product configuration and run on actual hardware if you are familiar with running components on other product configurations.
Add the server to your configuration and build:
fx set core.x64 --with //examples/fidl/dart/server && fx build
Ensure fx serve
is running in a separate tab and connected to an instance of Fuchsia (e.g. running in QEMU using fx qemu
), then run the server:
Note: The component should be referenced by its [URL][glossary.component url], which is determined with the [fuchsia-pkg://][glossary.fuchsia-pkg URL]
scheme. The package name in the URL matches the package_name
field in the fuchsia_package
declaration, and the manifest path in meta/
matches the target name of the fuchsia_component
.
fx shell run fuchsia-pkg://fuchsia.com/echo-rust-dart#meta/echo-server.cmx
Import the required dependencies in lib/main.dart
:
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/server/lib/main.dart" region_tag="imports" %}
Add the following to lib/main.dart
, above the main()
function:
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/server/lib/main.dart" region_tag="impl" %}
The implementation consists of the following elements:
EchoString
replies with the request value by returning it.SendString
returns void
since this method does not have a response. Instead, the implementation sends an OnString
event containing the request data._onStringStreamController
, which is used to implement the abstract onString
method. The FIDL runtime will subscribe to the stream returned by this method, sending incoming events to the client. The server can therefore send an OnString
event by sending an event on the stream.You can verify that the implementation is correct by running:
fx build
To run 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 an EchoBinding
.
The EchoBinding
is a class 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.
This complete process is described in further detail in the Life of a protocol open.
First, the code initializes the EchoBinding
as mentioned above:
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/server/lib/main.dart" region_tag="main" highlight="4,5,6,7" %}
In order to run, a binding needs two things:
The binding binds itself to a channel and implementation when the server receives a request to connect to an Echo
server.
Then, the code calls the component manager to expose the Echo
FIDL protocol to other components:
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/dart/server/lib/main.dart" region_tag="main" highlight="10,11,12,13,14,15,16,17" %}
It does so using the fuchsia_services
package, which provides an API to access the startup context of the component. Specifically, each component receives a ComponentContext
that the component can use to both access capabilties from other components and expose capabilities to other components. The call to sys.ComponentContext.create()
obtains an instance of the component's context, and the outgoing
property is used to expose the Echo
protocol and later serveFromStartupInfo()
.
In order to add a service, the outgoing context needs to know:
fidl.InterfaceRequest<Echo>
. This is a type-safe wrapper around a channel.InterfaceRequest
indicates that this is the server end of a channel (i.e. a client is connected to the remote end of the channel)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 name of the service is specified as the associated service name, and the handler is just a function that takes channel sent from the client and binds it to the EchoBinding
.
The server uses the fuchsia_logger
to log information. The logger needs to be initialized first using setupLogger()
, then information can be logged using log.info
or other methods corresponding to the various log levels.
Build:
fx build
Then run the server:
fx shell run fuchsia-pkg://fuchsia.com/echo-dart-server#meta/echo-server.cmx
You should see server hanging and the startup log using fx log
. This is expected because an event loop to handle incoming requests is running. The next step will be to write a client for the server.