Remote Call Framework 3.1
Client-side Programming

Making Remote Calls

Remote calls are always made through a RcfClient<> instance. Once you have defined a RCF interface:

RCF_BEGIN(I_PrintService, "I_PrintService")
RCF_METHOD_V1(void, Print, const std::string &)
RCF_END(I_PrintService)

, you can then instantiate a RcfClient<> and call a method on it:

RcfClient<I_PrintService> client((RCF::TcpEndpoint(port)));
client.Print("Hello World");

A RcfClient<> controls a single network connection to the server, and can only be used by one thread at a time. It will automatically connect to the server, using the endpoint parameter passed to the RcfClient<> constructor, when the first remote call is made. When the RcfClient<> is destroyed, the network connection to the server is closed.

Client Stubs

The client-side of a remote call is controlled through RCF::ClientStub. Every RcfClient<> instance contains a RCF::ClientStub, which can be accessed by calling getClientStub().

RCF::ClientStub & clientStub = client.getClientStub();

Almost all client-side configuration of remote calls, is done through RCF::ClientStub.

Transport Access

You can use RCF::ClientStub::getTransport() to access the underlying transport of a ClientStub.

RCF::ClientTransport & transport = client.getClientStub().getTransport();

There are a number of transport specific settings you can configure (see RCF::ClientTransport).

Transport level connection and disconnection from a server is normally handled automatically. However, you can also connect and disconnect manually by calling RCF::ClientStub::connect() and RCF::ClientStub::disconnect().

// Connect to server.
client.getClientStub().connect();
// Disconnect from server.
client.getClientStub().disconnect();

You can use ClientStub::releaseTransport() and ClientStub::setTransport() to move a transport from one client stub to another:

RcfClient<I_AnotherInterface> client2( client.getClientStub().releaseTransport() );
client2.AnotherPrint("Hello World");
client.getClientStub().setTransport( client2.getClientStub().releaseTransport() );
client.Print("Hello World");

This is useful if you want to use the same connection to make calls on different interfaces.

Timeouts

There are two important timeout settings that affect how a remote call is executed.

The first is the connect timeout, configured through RCF::ClientStub::setConnectTimeoutMs(). This setting determines how long the client will wait while attempting to establish a network connection to the server. You can set a custom connect timeout for a particular RcfClient<> by calling RCF::ClientStub::setConnectTimeoutMs(), or you can set a default connect timeout for all RcfClient<> instances, by calling RCF::globals()::setDefaultConnectTimeoutMs().

The second is the remote call timeout, configured through RCF::ClientStub::setRemoteCallTimeoutMs(). This setting determines how long the client will wait, after connecting, for a remote call to complete. Just as for connect timeouts, you can either set a remote call timeout on a particular RcfClient<> by calling RCF::ClientStub::setRemoteCallTimeoutMs(), or you can set a default remote call timeout for all RcfClient<> instances, by calling RCF::globals().setDefaultRemoteCallTimeoutMs().

Both timeouts are specified in milliseconds.

Pinging

The ClientStub::ping() method can be used, to determine if the client has a viable connection to the server. A ping behaves exactly as a remote call with no in or out parameters, and is subject to the same timeouts.

// Ping the server.
client.getClientStub().ping();

Client Progress Notification

By default, remote calls are executed synchronously. This means that while a remote call is executing, the client thread is blocked. You can, however, configure the client to periodically report progress to a a custom client progress callback function. From witin the progress callback function, you can then execute application specific code, for example to display a progress bar, refresh the application GUI, or cancel the call.

std::uint32_t progressCallbackIntervalMs = 500;
auto progressCallback = [&](const RCF::RemoteCallProgressInfo& info, RCF::RemoteCallAction& action)
{
// Set to RCF::Rca_Cancel to cancel the call.
};
client.getClientStub().setRemoteCallProgressCallback(
progressCallback,
progressCallbackIntervalMs);
// While the call is executing, the progress callback will be called every 500ms.
client.Print("Hello World");

Asynchronous remote calls (see Asynchronous Invocation), are another way of regaining control during a remote call.

Remote Call Mode

Each RCF client has a remote call mode associated with it. There are two fundamental remote call modes:

  • Two-way calls (RCF::Twoway) do not complete until the client receives a reply from the server. Two-way calls are the default remote call mode.
  • One-way calls (RCF::Oneway) complete as soon as the request has been sent to the server. The server does not send any response to a one-way call.

You can use RCF::ClientStub::setRemoteCallMode() to set the remote call mode.

client.getClientStub().setRemoteCallMode(RCF::Oneway);
client.Print("Hello World");
client.getClientStub().setRemoteCallMode(RCF::Twoway);
client.Print("Hello World");

The remote call mode can also be specified as part of a remote call, as the first argument of the remote call:

client.Print(RCF::Oneway, "Hello World");
client.Print(RCF::Twoway, "Hello World");

In this case, the remote call mode specified as part of the call, overrides the mode set by RCF::ClientStub::setRemoteCallMode().

Batched One-way Calls

RCF supports batched one-way calls, as an extension to one-way calls.

When a RcfClient<> is configured to make one-way calls, a network message is sent to the server for each remote call that is made. If a large number of calls are being made, you can used batched one-way calls to coalesce multiple one-way messages into a single network message, while still executing the remote calls in sequence on the server.

This can result in significant performance gains, depending on the volume and frequency of messages being sent.

To configure batched one-way calls, use the RCF::ClientStub::enableBatching(), RCF::ClientStub::disableBatching() and RCF::ClientStub::flushBatch() functions:

client.getClientStub().enableBatching();
// Automatically send batch when message size approaches 50kb.
client.getClientStub().setMaxBatchMessageLength(1024*50);
for (std::size_t i=0; i<100; ++i)
{
client.Print("Hello World");
}
// Send final batch.
client.getClientStub().flushBatch();

Custom Request and Response Data

Normally the application data transferred from the client to the server in a remote call is contained in the parameters of the remote call. You can also transfer extra information by setting the user data field of the remote call request. This data can then be accessed on the server, along with the parameters of the remote call.

Similarly, server code can set the user data field of a remote call response, and have that data transferred back to the client along with the parameters of the remote call.

The user data must be specified as a string.

class PrintService
{
public:
void Print(const std::string & s)
{
std::cout << "I_PrintService service: " << s << std::endl;
std::string customRequestData = session.getRequestUserData();
std::cout << "Custom request data: " << customRequestData << std::endl;
std::string customResponseData = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6";
session.setResponseUserData(customResponseData);
}
};
client.getClientStub().setRequestUserData( "e6a9bb54-da25-102b-9a03-2db401e887ec" );
client.Print("Hello World");
std::string customReponseData = client.getClientStub().getResponseUserData();
std::cout << "Custom response data: " << customReponseData << std::endl;

The user data fields can be used to implement custom authentication schemes where a token needs to be passed along as part of every remote call, but you don't want to have the token to be part of your remote call interface.

Copy semantics

RcfClient<> objects can be copied. However, each copy of a RcfClient<> object will establish its own network connection to the server. So the following code will establish 3 network connections to the server:

RcfClient<I_PrintService> client1(( RCF::TcpEndpoint(port) ));
RcfClient<I_PrintService> client2(client1);
RcfClient<I_PrintService> client3;
client3 = client1;
client1.Print("Hello World");
client2.Print("Hello World");
client3.Print("Hello World");