On the server-side, RCF::RcfServer
is the fundamental class responsible for dispatching remote calls from clients. A RcfServer
contains one or more transports, on which it listens for remote calls from clients. A RcfServer
also exposes one or more servant bindings, to which it dispatches remote calls that come in from clients.
A minimal example of a server was shown in the Tutorial. First, a RcfServer
is created with a TCP transport:
Then a servant binding is configured for the I_PrintService
interface:
Finally, the server is started and begins to respond to remote calls from clients:
A RcfServer
listens on one or more transports, for remote calls from clients. If you are setting up a server with only a single transport, you can supply the corresponding RCF::Endpoint
parameter to the RcfServer
constructor:
Alternatively, to configure multiple transports, you can use RCF::RcfServer::addEndpoint()
:
To perform additional transport-related configuration, you can capture the return value from RCF::RcfServer::addEndpoint()
:
Servant objects are responsible for the actual server functionality of your application. When a RcfServer
receives a remote call request from a client, it uses the servant binding name specified in the request to locate the relevant servant binding, and then dispatches the remote call to the relevant function on that servant object.
Servant objects are always bound to a RCF interface. To create a servant binding, use RCF::RcfServer::bind<>()
. Each binding on the server is identified by its binding name, which is normally the runtime name of the RCF interface used in the binding.
So the following code:
, creates a servant binding with the servant binding name "I_PrintService"
.
The servant binding name can also be set explicitly:
Every remote call request from a client includes a servant binding name, which is used by the server to dispatch the remote call. The default servant binding name supplied by a RCF client is the runtime name of the RCF interface it is using. So the following client code:
, makes a remote call which is dispatched by the server to a servant object with the servant binding name "I_PrintService"
.
The client can also set the servant binding name explicitly:
, to dispatch the call to the nominated servant binding.
A RcfServer
instance will not start dispatching remote calls until RCF::RcfServer::start()
is called.
The server can be stopped manually by calling RCF::RcfServer::stop()
:
The server will be stopped automatically if the RcfServer
object goes out of scope.
By default, a RcfServer
will use a single thread to dispatch calls across all its transports. This behavior can be modified by explicitly assigning a thread pool to the RcfServer
:
RCF::ThreadPool
can be configured to use a fixed number of threads, or a varying number of threads depending on server load:
A thread pool assigned to the RcfServer
will be shared by all the transports of that RcfServer
.
You can also use RCF::ServerTransport::setThreadPool()
to assign thread pools to specific transports:
RCF creates a server-side RCF::RcfSession
object for each connection to the server. The RcfSession
object has a lifetime that matches that of the client connection, and provides a mechanism for server-side code to persist state across remote calls on the same client connection.
From within a servant object executing a remote call, you can access the RcfSession
by calling RCF::getCurrentRcfSession()
:
Session objects are application C++ objects that are stored in the RCF session, and thus persisted across remote calls on the same connection. A typical use case for session objects is to associate application specific information with a connection. For example, application logic may require that after a client connects, the first thing it should do is call a Login()
method. If the Login()
method succeeds, the authenticated state of the connections needs to be persisted so that subsequent calls on the same connection can access it.
The following functions provide access to session objects:
RCF::RcfSession::createSessionObject<>()
RCF::RcfSession::getSessionObject<>()
RCF::RcfSession::querySessionObject<>()
RCF::RcfSession::deleteSessionObject<>()
Session objects can be of arbitrary type, and their lifetime is controlled by the lifetime of the client connection. When the client connection is closed, any session objects associated with it are destroyed.
For an example of using session objects to store authentication state, see Access Control.
As described in Client-side Programming, a remote call can have extra user data associated with it, apart from the parameters of the remote call. Both the remote call request and the remote call response can carry such data, and you can use the following RcfSession
functions to access that data:
RCF::RcfSession::getRequestUserData()
RCF::RcfSession::setRequestUserData()
RCF::RcfSession::getResponseUserData()
RCF::RcfSession::setResponseUserData()
You can use RcfSession
to find out a number of things about the current client connection, including:
For more information, see the reference documentation for RCF::RcfSession
.
RCF allows you to apply access controls to individual servant bindings on your server. The access control is implemented as a user-defined callback function, in which you can apply application-specific logic to determine whether a client connection should be allowed to access a particular servant binding.
For example:
The access control callback will be invoked by the RcfServer
each time a client tries to call a method on that servant binding.
From the access control callback you can inspect the current session and determine whether it should be granted access to the servant. Once authentication is granted, you will probably want to store the authentication state in a session object, so it can be easily reused on subsequent calls:
This example uses the access control callback to inspect the transport protocol the client is using, and uses that to determine the identity of the client.
In some situations you may want the client to provide extra authentication information, beyond what is available though the transport protocol. This typically means having the equivalent of a Login()
method on the interface, that needs to be called before any other method on the interface. The access control callback can be used to verify that Login()
is called before any other method:
In this case, the access control callback allows calls to Login()
to go through, while for any other method on the I_PrintService
interface, it checks for the existence of the authentication state that the Login()
call creates.
The Login()
method is identified in the access control callback by its method ID - in this case 0, as it is the first method on the interface. Method ID's are assigned in incremental order, from 0, so if for example Login()
had been the third method on the interface, it would have had a method ID of 2.
Server objects are application specific objects that are created by your server-side code and stored in the RcfServer
. Unlike session objects (which are stored in RcfSession
), server objects persist and are accessible outside of the RCF session in which they were created. The lifetime of server objects is managed by RCF::RcfServer
, using a garbage collection policy.
Server objects are manipulated with the following functions:
Here is how we would create and update a server-wide session object for authenticated users:
This code creates a PrintServiceSession
object for each authenticated user. The PrintServiceSession
object will be shared across all connections associated with that authenticated user.