Usually remote calls are made from a client to a server. RCF also implements a callback connection concept, allowing the server to take over a connection and use it to make remote calls back to the client.
Remote calls over callback connections function just like remote calls over regular connections, except that callback connections cannot be automatically re-connected.
On the client-side, to create a callback connection to receive calls on,
use RCF::createCallbackConnection()
:
// Client-side - start a callback server. RCF::RcfServer callbackServer( RCF::TcpEndpoint(0) ); HelloWorldImpl helloWorldImpl; callbackServer.bind<I_HelloWorld>(helloWorldImpl); callbackServer.start(); // Client-side - create callback connection. RcfClient<I_HelloWorld> client(( RCF::TcpEndpoint(port) )); RCF::createCallbackConnection(client, callbackServer);
Note that you need a callback server to receive calls through, and a RcfClient<>
instance with which to connect to the remote server.
On the server-side, use RcfServer::setOnCallbackConnectionCreated()
to receive notification when a callback
connection is created, and take control of the callback client transport:
// Server-side - taking control of callback connections. RCF::Mutex gCallbackTransportMutex; RCF::ClientTransportAutoPtr gCallbackTransportPtr; void onCallbackConnectionCreated( RCF::RcfSessionPtr sessionPtr, RCF::ClientTransportAutoPtr clientTransportPtr) { // Store the callback client transport in a global variable for later use. RCF::Lock lock(gCallbackTransportMutex); gCallbackTransportPtr = clientTransportPtr; }
RCF::RcfServer server( RCF::TcpEndpoint(0) ); server.setOnCallbackConnectionCreated(onCallbackConnectionCreated); server.start();
Once you have the callback client transport, you can use it to construct
an appropriate RcfClient<>
instance, and subsequently use that
to make calls back to the client:
// Server-side - wait for callback connection to be created. while (true) { RCF::sleepMs(1000); RCF::Lock lock(gCallbackTransportMutex); if (gCallbackTransportPtr.get()) { RcfClient<I_HelloWorld> callbackClient( gCallbackTransportPtr ); callbackClient.Print("Hello World"); break; } }
Typically you will want a server to maintain a list of callback connections
to clients, and make calls back to specific clients. To do this, the server
needs a mechanism by which to identify the callback connections. The best
way to do this is to have the client set some session-specific data, before
it calls RCF::createCallbackConnection()
.
Let's assume you want to identify your clients with a string - for example
a name. Before the client calls RCF::createCallbackConnection()
, it should call an application-defined
remote method on the server, to set the name of the client:
RCF_BEGIN(I_IdentifyClient, "I_IdentifyClient") RCF_METHOD_V1(void, SetClientName, const std::string&) RCF_END(I_IdentifyClient)
// Client-side - create callback connection. RcfClient<I_IdentifyClient> client(( RCF::TcpEndpoint(port) )); client.SetClientName("ABC-123"); RCF::createCallbackConnection(client, callbackServer);
The server implementation of SetClientName()
creates a session object in the RcfSession
of the connection, with the
client name:
class IdentifyClientImpl { public: void SetClientName(const std::string& clientName) { RCF::RcfSession & session = RCF::getCurrentRcfSession(); std::string & sessionClientName = session.createSessionObject<std::string>(); sessionClientName = clientName; } };
When the client subsequently calls RCF::createCallbackConnection()
, the RcfServer
can then retrieve the name of the client and associate the callback connection
with that name:
// Server-side - taking control of callback connections. RCF::Mutex gCallbackClientMapMutex; typedef RcfClient<I_HelloWorld> HelloWorldClient; typedef boost::shared_ptr<HelloWorldClient> HelloWorldClientPtr; std::map<std::string, HelloWorldClientPtr> gCallbackClientMap; void onCallbackConnectionCreated( RCF::RcfSessionPtr sessionPtr, RCF::ClientTransportAutoPtr clientTransportPtr) { std::string & clientName = sessionPtr->getSessionObject<std::string>(); // Store the callback client transport in a global variable for later use. RCF::Lock lock(gCallbackClientMapMutex); HelloWorldClientPtr hwClientPtr( new HelloWorldClient(clientTransportPtr) ); gCallbackClientMap[clientName] = hwClientPtr; }
Transport protocols configured on the original client-to-server connection are not carried over to the callback connection. If you want to use a transport protocol on the callback connection, it needs to be configured explicitly:
RcfClient<I_HelloWorld> callbackClient( gCallbackTransportPtr ); callbackClient.getClientStub().setTransportProtocol(RCF::Tp_Ntlm); callbackClient.Print("Hello World");