Implement a C++ FIDL server

Prerequisites

This tutorial builds on thedomain objects tutorial. For thefull set of FIDL tutorials, refer to theoverview.

Overview

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 methodof each kind: a one-way method, a two-way method, and an event:

@discoverableclosedprotocolEcho{strictEchoString(struct{valuestring:MAX_STRING_LENGTH;})->(struct{responsestring:MAX_STRING_LENGTH;});strictSendString(struct{valuestring:MAX_STRING_LENGTH;});strict->OnString(struct{responsestring:MAX_STRING_LENGTH;});};

For more on FIDL methods and messaging models, refer to theFIDL concepts page.

This document covers how to complete the following tasks:

Structure of the server example

The example code accompanying this tutorial is located in your Fuchsia checkoutat//examples/fidl/cpp/server. It consists of a servercomponent and its containing package. For more information about buildingcomponents, seeBuild components.

To get the server component up and running, there are three targets that aredefined in//examples/fidl/cpp/server/BUILD.gn:

  1. The raw executable file for the server. This produces a binary with thespecified output name that can run on Fuchsia:

    executable("bin"){output_name="fidl_echo_cpp_server"sources=["main.cc"]deps=["//examples/fidl/fuchsia.examples:fuchsia.examples_cpp",#Thislibraryisusedtologmessages."//sdk/lib/syslog/cpp",#Thislibraryisusedtopublishcapabilities,e.g.protocols,#tothecomponent'soutgoingdirectory."//sdk/lib/component/outgoing/cpp",#Thislibraryprovidesantheasynchronouseventloopimplementation."//sdk/lib/async-loop:async-loop-cpp",]}
  2. A component that is set up to run the server executable.Components are the units of software execution on Fuchsia. A component isdescribed by its manifest file. In this casemeta/server.cmlconfiguresecho-server as an executable component which runsfidl_echo_cpp_server in:bin.

    fuchsia_component("echo-server") {  component_name = "echo_server"  manifest = "meta/server.cml"  deps = [ ":bin" ]}

    The server component manifest is located at//examples/fidl/cpp/server/meta/server.cml. The binary name in themanifest must match the output name of theexecutable defined inBUILD.gn.

    {include:["syslog/client.shard.cml"],// Information about the program to run.program:{// Use the built-in ELF runner.runner:"elf",// The binary to run for this component.binary:"bin/fidl_echo_cpp_server",},// Capabilities provided by this component.capabilities:[{protocol:"fuchsia.examples.Echo"},],expose:[{protocol:"fuchsia.examples.Echo",from:"self",},],}
  3. The component is then put into a package, which is the unit of softwaredistribution on Fuchsia. In this case, the package just contains asingle component.

    fuchsia_package("echo-cpp-server"){package_name="echo-cpp-server"deps=[":echo-server"]}

Building the server

You may build the server package via the following:

  1. Add the server to your build configuration. This only needs to be done once:

    fxsetcore.x64--with//examples/fidl/cpp/server
  2. Build the server package:

    fxbuild//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 appropriateproduct configuration for your hardware.

Implement the FIDL protocol

EchoImpl implements the server request handler for thefuchsia.examples/Echoprotocol. To do so,EchoImpl inherits from the generated pure virtual serverinterfacefidl::Server<fuchsia_examples::Echo>, and overrides its pure virtualmethods corresponding to every one way and two way call:

classEchoImpl:publicfidl::Server<fuchsia_examples::Echo>{public:// The handler for `fuchsia.examples/Echo.EchoString`.//// For two-way methods (those with a response) like this one, the completer is// used complete the call: either to send the reply via |completer.Reply|, or// close the channel via |completer.Close|.//// |EchoStringRequest| exposes the same API as the request struct domain// object, that is |fuchsia_examples::EchoEchoStringRequest|.voidEchoString(EchoStringRequest&request,EchoStringCompleter::Sync&completer)override{// Call |Reply| to reply synchronously with the request value.completer.Reply({{.response = request.value()}});}// The handler for `fuchsia.examples/Echo.SendString`.//// For fire-and-forget methods like this one, the completer is normally not// used, but its |Close(zx_status_t)| method can be used to close the channel,// either when the protocol has reached its intended terminal state or the// server encountered an unrecoverable error.//// |SendStringRequest| exposes the same API as the request struct domain// object, that is |fuchsia_examples::EchoSendStringRequest|.voidSendString(SendStringRequest&request,SendStringCompleter::Sync&completer)override{ZX_ASSERT(binding_ref_.has_value());// Handle a SendString request by sending an |OnString| event (an// unsolicited server-to-client message) back to the client.fit::resultresult=fidl::SendEvent(*binding_ref_)->OnString({request.value()});if(!result.is_ok()){FX_LOGS(ERROR) <<"Error sending event: " <<result.error_value();}}// ... other methods from examples/fidl/cpp/server/main.cc omitted, to be covered later.private:// `ServerBindingRef` can be used to:// - Control the binding, such as to unbind the server from the channel or//   close the channel.// - Send events back to the client.// See the documentation comments on |fidl::ServerBindingRef|.std::optional<fidl::ServerBindingRef<fuchsia_examples::Echo>>binding_ref_;};
Note: in this tutorial theEchoString handler replies synchronously. Forasynchronous replies, seeresponding to requests asynchronously.

Bind the implementation to a server endpoint

Implementing the request handlers is only half the story. The server needs to beable to monitor new messages that arrives on aserver endpoint. To do this,EchoImpl defines two moremethods: aBindSelfManagedServer static factory function that creates anewEchoImpl instance to handle requests on a new server endpointfidl::ServerEnd<fuchsia_examples::Echo>, and anOnUnbound method thatis called when the connection is torn down:

/* Inside `class EchoImpl {`... */// Bind a new implementation of |EchoImpl| to handle requests coming from// the server endpoint |server_end|.staticvoidBindSelfManagedServer(async_dispatcher_t*dispatcher,fidl::ServerEnd<fuchsia_examples::Echo>server_end){// Create a new instance of |EchoImpl|.std::unique_ptrimpl=std::make_unique<EchoImpl>();EchoImpl*impl_ptr=impl.get();// |fidl::BindServer| takes a FIDL protocol server implementation and a// channel. It asynchronously reads requests off the channel, decodes them// and dispatches them to the correct handler on the server implementation.//// The FIDL protocol server implementation can be passed as a// |std::shared_ptr|, |std::unique_ptr|, or raw pointer. For shared and// unique pointers, the binding will manage the lifetime of the// implementation object. For raw pointers, it's up to the caller to ensure// that the implementation object outlives the binding but does not leak.//// See the documentation comment of |fidl::BindServer|.fidl::ServerBindingRefbinding_ref=fidl::BindServer(dispatcher,std::move(server_end),std::move(impl),std::mem_fn(&EchoImpl::OnUnbound));// Put the returned |binding_ref| into the |EchoImpl| object.impl_ptr->binding_ref_.emplace(std::move(binding_ref));}// This method is passed to the |BindServer| call as the last argument,// which means it will be called when the connection is torn down.// In this example we use it to log some connection lifecycle information.// Production code could do more things such as resource cleanup.voidOnUnbound(fidl::UnbindInfoinfo,fidl::ServerEnd<fuchsia_examples::Echo>server_end){// |is_user_initiated| returns true if the server code called |Close| on a// completer, or |Unbind| / |Close| on the |binding_ref_|, to proactively// teardown the connection. These cases are usually part of normal server// shutdown, so logging is unnecessary.if(info.is_user_initiated()){return;}if(info.is_peer_closed()){// If the peer (the client) closed their endpoint, log that as INFO.FX_LOGS(INFO) <<"Client disconnected";}else{// Treat other unbind causes as errors.FX_LOGS(ERROR) <<"Server error: " <<info;}}

Publish the protocol implementation

A component that implements a FIDL protocol can expose that FIDLprotocol to other components. This is done by publishing the protocolimplementation to the component'soutgoing directory . This complete process isdescribed in further detail in theLife of a protocol open.We can usecomponent::OutgoingDirectory from the C++ component runtime libraryto perform the heavy lifting.

To depend on the component runtime library:

executable("bin"){output_name="fidl_echo_cpp_server"sources=["main.cc"]deps=["//examples/fidl/fuchsia.examples:fuchsia.examples_cpp",#Thislibraryisusedtologmessages."//sdk/lib/syslog/cpp",#Thislibraryisusedtopublishcapabilities,e.g.protocols,#tothecomponent'soutgoingdirectory."//sdk/lib/component/outgoing/cpp",#Thislibraryprovidesantheasynchronouseventloopimplementation."//sdk/lib/async-loop:async-loop-cpp",]}

Import the library at the top ofexamples/fidl/cpp/server/main.cc:

#include <fidl/fuchsia.examples/cpp/fidl.h>#include <lib/async-loop/cpp/loop.h>#include <lib/component/outgoing/cpp/outgoing_directory.h>#include <lib/syslog/cpp/log_settings.h>#include <lib/syslog/cpp/macros.h>

Serve the component's outgoing directory:

intmain(intargc,constchar**argv){// The event loop is used to asynchronously listen for incoming connections// and requests from the client. The following initializes the loop, and// obtains the dispatcher, which will be used when binding the server// implementation to a channel.async::Looploop(&kAsyncLoopConfigNeverAttachToThread);async_dispatcher_t*dispatcher=loop.dispatcher();fuchsia_logging::LogSettingsBuilderbuilder;builder.WithDispatcher(dispatcher).BuildAndInitialize();// Create an |OutgoingDirectory| instance.//// The |component::OutgoingDirectory| class serves the outgoing directory for// our component. This directory is where the outgoing FIDL protocols are// installed so that they can be provided to other components.component::OutgoingDirectoryoutgoing=component::OutgoingDirectory(dispatcher);// 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.zx::resultresult=outgoing.ServeFromStartupInfo();if(result.is_error()){FX_LOGS(ERROR) <<"Failed to serve outgoing directory: " <<result.status_string();return-1;}// ...

Serve the protocol

The server then registers the Echo protocol usingoutgoing.AddProtocol.

intmain(intargc,constchar**argv){// The event loop is used to asynchronously listen for incoming connections// and requests from the client. The following initializes the loop, and// obtains the dispatcher, which will be used when binding the server// implementation to a channel.async::Looploop(&kAsyncLoopConfigNeverAttachToThread);async_dispatcher_t*dispatcher=loop.dispatcher();fuchsia_logging::LogSettingsBuilderbuilder;builder.WithDispatcher(dispatcher).BuildAndInitialize();// Create an |OutgoingDirectory| instance.//// The |component::OutgoingDirectory| class serves the outgoing directory for// our component. This directory is where the outgoing FIDL protocols are// installed so that they can be provided to other components.component::OutgoingDirectoryoutgoing=component::OutgoingDirectory(dispatcher);// 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.zx::resultresult=outgoing.ServeFromStartupInfo();if(result.is_error()){FX_LOGS(ERROR) <<"Failed to serve outgoing directory: " <<result.status_string();return-1;}// Register a handler for components trying to connect to fuchsia.examples.Echo.result=outgoing.AddUnmanagedProtocol<fuchsia_examples::Echo>([dispatcher](fidl::ServerEnd<fuchsia_examples::Echo>server_end){FX_LOGS(INFO) <<"Incoming connection for "                      <<fidl::DiscoverableProtocolName<fuchsia_examples::Echo>;EchoImpl::BindSelfManagedServer(dispatcher,std::move(server_end));});if(result.is_error()){FX_LOGS(ERROR) <<"Failed to add Echo protocol: " <<result.status_string();return-1;}FX_LOGS(INFO) <<"Running C++ echo server with natural types";// This runs the event loop and blocks until the loop is quit or shutdown.// See documentation comments on |async::Loop|.loop.Run();return0;}

The call toAddProtocol 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 tofuchsia.examples.Echo,outgoing will call the lambda function that wecreated with a server endpoint corresponding to the client endpoint from theclient, and this lambda function will call theEchoImpl::BindSelfManagedServerdetailed above to bind the server endpoint to a new instance ofEchoImpl.

Our main method will keep listening for incoming requests on theasync loop.

Test the server

Afterbuilding the server, you may run the example on a runninginstance of Fuchsia emulator via

ffxcomponentrun/core/ffx-laboratory:echo-serverfuchsia-pkg://fuchsia.com/echo-cpp-server#meta/echo_server.cm
Note: Components are resolved using theircomponent 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 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 sendsEcho protocol requests.For now, you can simply terminate the server component:

ffxcomponentdestroy/core/ffx-laboratory:echo_server
Note: Component instances are referenced by theircomponent moniker , which is determined by their location inthecomponent instance tree

Serve requests using wire domain objects

The above tutorial implements a server withnatural domain objects: the server receives requestsrepresented in natural domain objects, and sends replies encoded from naturaldomain objects. When optimizing for performance and heap allocation, one mayimplement a server that speakswire domain objects, i.e. a wireserver. Here is theEchoImpl rewritten to use wire domain objects:

classEchoImplfinal:publicfidl::WireServer<fuchsia_examples::Echo>{public:// The handler for `fuchsia.examples/Echo.EchoString`.//// For two-way methods (those with a response) like this one, the completer is// used complete the call: either to send the reply via |completer.Reply|, or// close the channel via |completer.Close|.//// |EchoStringRequestView| exposes the same API as a pointer to the request// struct domain object, that is// |fuchsia_examples::wire::EchoEchoStringRequest*|.voidEchoString(EchoStringRequestViewrequest,EchoStringCompleter::Sync&completer)override{// Call |Reply| to reply synchronously with the request value.completer.Reply(request->value);}// The handler for `fuchsia.examples/Echo.SendString`.//// For fire-and-forget methods like this one, the completer is normally not// used, but its |Close(zx_status_t)| method can be used to close the channel,// either when the protocol has reached its intended terminal state or the// server encountered an unrecoverable error.//// |SendStringRequestView| exposes the same API as a pointer to the request// struct domain object, that is// |fuchsia_examples::wire::EchoSendStringRequest*|.voidSendString(SendStringRequestViewrequest,SendStringCompleter::Sync&completer)override{// Handle a SendString request by sending an |OnString| event (an// unsolicited server-to-client message) back to the client.fidl::Statusstatus=fidl::WireSendEvent(binding_)->OnString(request->value);if(!status.ok()){FX_LOGS(ERROR) <<"Error sending event: " <<status.error();}}// ... |BindSelfManagedServer| etc omitted. Those stay the same.};

The relevant classes and functions used in a wire server have similar shapes tothose used in a natural server. When a different class or function is calledfor, the wire counterpart is usually prefixed withWire. There are alsosmall differences in pointers vs references and argument structure:

  • The server interface implemented by a natural server isfidl::Server<fuchsia_examples::Echo>. The server interface implemented by awire server isfidl::WireServer<fuchsia_examples::Echo>.

  • The handler function in a natural server takes a reference to the requestmessage. TheReply method takes a single argument that is the responsepayload domain object:

    voidEchoString(EchoStringRequest&request,EchoStringCompleter::Sync&completer)override{// Call |Reply| to reply synchronously with the request value.completer.Reply({{.response = request.value()}});}

    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, theReplymethod flattens the list of struct fields in the response payload intoseparate arguments (here, a singlefidl::StringView argument):

    voidEchoString(EchoStringRequestViewrequest,EchoStringCompleter::Sync&completer)override{// Call |Reply| to reply synchronously with the request value.completer.Reply(request->value);}
  • The function to send events with natural types isfidl::SendEvent. Thefunction to send events with wire types isfidl::WireSendEvent. Structfields are also flattened into separate arguments when sending an event.

The samefidl::BindServer function may be used to bind either a natural serveror a wire server.

The full example code for a wire server is located in your Fuchsia checkoutat//examples/fidl/cpp/server/wire.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-05-20 UTC.