PrevUpHomeNext

Remote Calls - Client-side

Remote call semantics
Pinging
Client progress callbacks
Per-request user data
Access to underlying transport
Copy semantics

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

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

You can use ClientStub::setRemoteCallSemantics() to set the calling semantics of a remote call.

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

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

  • Twoway calls (RCF::Twoway) are the default. A twoway call does not complete until the client receives a reply from the server.
  • A oneway call (RCF::Oneway) is complete as soon as the request has been sent to the server. The server will not send any response to a oneway call.

By default a twoway call will cause the calling thread to wait until a response is received from the server. It is also possible to receive the response asynchronously, thus freeing the calling thread to perform other tasks - see Asynchronous Remote Calls. Asynchronous semantics can also be used on oneway calls, to prevent the calling thread from blocking if the local network send buffers are full.

Calling semantics can also be provided on a call-by-call basis, as the first argument of the remote call:

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

RCF also provides batched oneway calls, as an optimization for oneway calls. Normally, when a RcfClient<> is configured to make oneway 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, batched oneway calls can be configured, allowing multiple oneway calls to be coalesced into a single network message, and subsequently executed in sequence on the server.

To configure batched oneway calls, use the ClientStub::enableBatching(), ClientStub::disableBatching() and 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();

The ClientStub::ping() method can be used, to determine if the connection to the server is functional. 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();

RCF can be configured to periodically report progress to a custom client progress callback function during a twoway call. You can use the progress callback function to display a progress bar, repaint the application window, or even cancel the call itself.

void onRemoteCallProgress()
{
    // To cancel the call, throw an exception.
    throw std::runtime_error("Canceling remote call.");
}

boost::uint32_t progressCallbackIntervalMs = 500;

client.getClientStub().setRemoteCallProgressCallback(
    boost::bind(&onRemoteCallProgress),
    progressCallbackIntervalMs);

// While the call is in progress, onRemoteCallProgress() will be called every 500ms.
client.Print("Hello World");

Normally the data transferred from the client to the server in a remote call is contained in the arguments of the remote call. However, you can also supply extra information in the user data field of the remote call request. Similarly, the server can send extra information to the client by setting the the user data field of the remote call response.

class HelloWorldImpl
{
public:
    void Print(const std::string & s)
    {
        std::cout << "I_HelloWorld service: " << s << std::endl;

        RCF::RcfSession & session = RCF::getCurrentRcfSession();
        
        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.

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

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

Transport level connection and disconnection from a server is normally handled automatically. However, you can also connect and disconnect manually by calling ClientStub::connect() and 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.

RCF::RcfClient<> objects can be copied, put in containers, and so on. 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_HelloWorld> client1(( RCF::TcpEndpoint(port) ));

RcfClient<I_HelloWorld> client2(client1);

RcfClient<I_HelloWorld> client3;
client3 = client1;

client1.Print("Hello World");
client2.Print("Hello World");
client3.Print("Hello World");


PrevUpHomeNext