Click here to Skip to main content
Email Password   helpLost your password?

Please Note

This article is quite old now. The code samples still compile with current versions of RCF, but for up to date information, please refer to this article:

RCF - Interprocess Communication for C++

Introduction

The officially sanctioned way of making distributed function calls between C++ programs is to use CORBA, but for many applications, this is overkill. The CORBA specifications allow distributed function calls to be made between code written in any number of languages, and to make it all work, specialized tools need to be integrated into the build process, in order to translate object definitions written in CORBA's IDL to whichever native language is being used (C++, Java, etc.).

However, if we assume that the server and client are both written in the same language, let us assume C++, since it is possible to do away with these complexities. In particular, instead of elaborate definitions of interfaces and marshalling specifications, we can simply defer to C++.

Instead of separate IDL files with object interfaces, we specify the interfaces directly in C++ source code, using the preprocessor, and to marshal arguments across process boundaries, we use the native C++ serialization framework provided in the latest release of the Boost library.

A Simple Example

As an example, a simple echo server looks like this:

#include <RCF/RCF.hpp> 


RCF_BEGIN(I_Echo, "I_Echo")
  RCF_METHOD_R1(std::string, echo, const std::string &);
RCF_END(I_Echo);

class Echo
{
public:
  std::string echo(const std::string &msg) { return msg; }
};

int main()
{
  int port = 50001;
  RCF::RcfServer server(port);
  server.bind<I_Echo, Echo>();
  server.start();
  return 0;
}

And the client:

#include <RCF/RCF.hpp>


RCF_BEGIN(I_Echo, "I_Echo")
  RCF_METHOD_R1(std::string, echo, const std::string &);
RCF_END(I_Echo);

int main()
{
  std::cout << RcfClient<I_Echo>("localhost", 
                               50001).echo("my message");
  return 0;
}

The Boost.Serialization library is used to serialize parameters and return values. It handles standard types and containers automatically, and is easily extended to user defined classes. It also allows us to serialize pointers, with proper handling of polymorphic pointers and multiple pointers to single objects.

Basic Usage

There are three basic steps to using this framework:

  1. Use the RCF_xxx macros to define interfaces.
  2. Use the RcfServer class to expose objects that implement the interface.
  3. Use the RcfClient<> classes to invoke methods on the objects exposed by the server.

The interface definition macros are used as follows:

RCF_BEGIN( type, type_id )
  // ...

  RCF_METHOD_xx( return_type, name, ....):
  // ...

RCF_END( type )

type is the identifier for the interface, type_id is a string giving a runtime description of the interface. The RCF_METHOD_xx macros define the member functions, and are named according to the number of arguments and whether the return value is void or not. So, for a function func accepting two strings and returning an integer, we write:

  RCF_METHOD_R2(int, func, std::string, std::string);

and if the function has a void return type, we would instead write:

  RCF_METHOD_V2(void, func, std::string, std::string);

Dispatch IDs for each function are generated automatically; the first member function is numbered 0, the next one 1, and so on. So, the order in which the functions appear in the definition is important, unlike in CORBA, where dispatch IDs are based on the function name. The dispatch IDs are generated using templates and not any preprocessor __LINE__ trickery, so the interface does not change if blank lines are inserted. The maximum number of member functions that can appear between RCF_BEGIN() and RCF_END() is at the moment limited to 25, but this limit is arbitrary.

The purpose of the RCF_xxx macros is to define the class RcfClient<type>. This class serves as a client stub, from the user's point of view, but also has facilities that allow the framework to use it as a server stub. These macros can be used in any namespace, not just the global namespace.

Once we have defined an interface using the RCF_xxx macros, we can start a server and bind the interface to concrete objects:

{
  // create the server and tell it which port to listen on

  RCF::RcfServer server(port);

  // Interface is the identifer of the interface we're exporting,

  // Object is a type that implements that interface

    
  // one object for each client

  server.bind<Interface, Object>(); 

  // ... or one object shared by all clients

  Object object;
  server.bind<Interface>(object); 

  // tell the server to start listening for connections

  server.start();

  // ...


  // the server will shut down automatically as it goes out of scope

}

The objects are statically bound to the corresponding interface; there is no need for the object to derive from an interface class as is the case for traditional dynamic polymorphism. Instead, the compiler resolves the interface at compile time, which is not only more efficient, but also allows more flexible semantics.

The server can handle multiple simultaneous clients, even in single threaded mode, and can be stopped at any time. The lifetime of objects exposed by the server is determined by the number of current connections to the given object; once there are no more live connections to the object, a timeout is set, and when it expires, the object is deleted.

To make a client call, we instantiate the corresponding RcfClient<> template and pass the server IP and port number to the constructor. When the first remote method is called, the client then attempts to connect to the server, queries for the given object, invokes the requested member function of the remote object, and then returns the remote return value.

// define the interface

RCF_BEGIN(Interface, "Interface")
  RCF_METHOD_R2(int, add, int, int);
RCF_END(Interface);

// ...


{
  std::string ip = "localhost";
  int port = 50001;
  RcfClient<Interface> client(ip, port);
  
  // connect and call the add function

  int sum = client.add(1,1);
  
  // connection closed as we exit scope

}

Should any exceptions arise on the server side while invoking the requested object, an exception of type RCF::RemoteException will be propagated back to the client and thrown. Should any exceptions arise anywhere else on the server side, e.g., while serializing arguments, then the server will forcibly close the connection, and the client will throw an exception.

RCF will automatically handle a range of parameter types, including C++ primitive types (int, double, etc.), std::string, STL containers, and pointers and references to any of the previously mentioned types. Polymorphic pointers and references, and multiple pointers to single objects are correctly handled as well. Smart pointers are also supported (boost::shared_ptr, std::auto_ptr), and are the safest way of passing polymorphic parameters.

In CORBA, one can tag a parameter as in, out, or inout, depending on which direction(s) one wants the parameter to be marshaled. In RCF, the marshaling directions are deduced from the parameter type, according to the following conventions:

Value: in
Pointer: in
Const reference: in
Nonconst reference: inout

Nonconst reference to pointer: out

To use user-defined types as parameters or return values, some additional serialization code is needed. What that code is depends on which serialization protocols are being used; by default Boost.Serialization is used, and an example of passing a user-defined type would look like the following:

struct MyStruct
{
  int a;
  int b;
  int c;
  double d;
  std::string s;
  std::map <std::string, std::vector<std::string> > m;
  
  template<typename Archive>
  void serialize(Archive &archive, unsigned int version)
  {
    ar & a & b & c & d & s & m;
  }
  
};

RCF_BEGIN(MyInterface, "MyInterface")
  RCF_METHOD_R1(MyStruct, myfunc, const MyStruct &);
RCF_END(MyInterface);

Details

The server and client classes use BSD-style sockets to implement the networking, over TCP, and the whole framework has been compiled and tested on Linux, Solaris (x86 and SPARC) and Win32, using Visual C++ 7.1, Codewarrior 9.0, Borland C++ 5.5, and GCC 3.2. Building RCF requires v. 1.32.0 or later of the Boost library, although the only parts of Boost that need to be built are Boost.Serialization, and, for multithreaded builds, Boost.Threads. Multithreaded builds are enabled by defining RCF_USE_BOOST_THREADS before including any RCF headers.

To use RCF in your own application, you'll need to include the src/RCF.cpp file among the sources of the application, and link to the necessary libraries from Boost, along with OS-specific socket libraries (on Windows that would be ws2_32.lib, on Linux libnsl, etc.).

I've included a demo project for Visual Studio .NET 2003, which includes everything needed to compile, link, and run a server/client pair, with the exception of the Boost library, which needs to be downloaded and unzipped, but no building is needed.

Performance, as measured in requests/second, is highly dependent on the serialization protocol, and also on the compiler being used. Before turning to Boost.Serialization, I used a serialization framework of my own, with which I could clock around 3000 minimal requests/sec. using Visual C++ 7.1, and 3300 requests/sec. with Codewarrior 9.0, on a loopback connection on a 1400Mhz, 384Mb PC running Windows XP. GCC 3.2, on the other hand, was far slower. Using Boost.Serialization, however, I've been nowhere near these numbers; on average, it's around five times slower.

Conclusion

RMI is a well known concept in Java circles, what I've done here is to do something similar in C++, without all the complications of CORBA. If you like it, please tell me, if you don't, well, please tell someone else.... Jokes aside, any and all feedback is appreciated, all I ask is that if you grade the article, and do so with a low grade, then please leave an explanatory comment!

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
General100% custom RMI...
Alexe123
6:32 12 Dec '08  
Hi,
I am trying to implement a light weight RMI to be used in Java<---->Java applications...

At this time I have no idea how to make it (RPC part). I am thinking to implement it using native calls and not relying in Java (implement it in C and export the call to java).

Is this possible? Any tips for the startup of the project?

Thanks you very much!

Alex
GeneralRe: 100% custom RMI...
Jarl Lindrud
2:34 23 Dec '08  
Sure it's possible... But you'll want to use a newer version of RCF than the one this article refers to. Have a look at

http://www.codeproject.com/KB/threads/Rcf_Ipc_For_Cpp.aspx

Regards,
Jarl.
GeneralRe: 100% custom RMI...
Alexe123
7:30 6 Mar '09  
Hi again,

I forgot to say that the lightweight for of RMI it to be used with also a custom implementation of Jini...

Your other article seems very interesting to replace RMI. Now I have to see if it can be a good base for Jini...
Any comment on this by you would be very appreciated for me.

Thank you very much

Alex
QuestionInstallation of RCF on linux with gcc 3.4
orlandocabral
5:53 5 Dec '08  
Hello,
This seem very easy to use, much simpler than RMI.
But i do not manage to put it working under linux.
I have downloaded BOOST (boost_1_37_0.tar.bz2), compile, and test it. It is working.

But regarding RCF I do not know how to use it. According to the google.code site where RCF 1.0 is available, and using the getting started tutorial you should compile RCF.cpp and it is working.
Here it is what I did after boost:
compiling RCF 1.0
g++ -I /home/din-por/Documents/boost_1_37_0 -I /home/din-por/Documents/RCF-1.0/include -DRCF_USE_BOOST_SERIALIZATION RCF.cpp -o RCP

compiling your example that I name server.cpp:
g++ -I /home/din-por/Documents/boost_1_37_0 -I /home/din-por/Documents/RCF-1.0/include -DRCF_USE_BOOST_SERIALIZATION server.cpp -o server

But it does not generate anything. What's wrong??
Regards,
Orlando
AnswerRe: Installation of RCF on linux with gcc 3.4
Jarl Lindrud
16:11 5 Dec '08  
Hi,

This article is quite old, there is a newer one here:

http://www.codeproject.com/KB/threads/Rcf_Ipc_For_Cpp.aspx

As far as getting started, it's easier not to use Boost.Serialization, as if you do, you need to build it separately. So if you remove the -DRCF_USE_BOOST_SERIALIZATION option from your command lines, you should be OK.

I'm not a g++ command line expert, but the two commands above should generate the files RCP and server, but then you need to link them together as well, into an executable. You should be able to do it all in one command, like this:

g++ -I /home/din-por/Documents/boost_1_37_0 -I /home/din-por/Documents/RCF-1.0/include RCF.cpp server.cpp

That should get you an executable, probably named a.out .

Regards,
Jarl.
GeneralConcurent server call
suiram40
18:28 1 Oct '08  
I am playing with the sample and I just spawned couple of threads in the existent sample
and the things behave crashy.


void ThrFoo(void* pv)
{
char s[32];
std::vectorstring xmlns:std="#unknown"> v;
for(int i=0;i<32;i++){
sprintf(s," %d ",i);
v.push_back(s);
}
RcfClient c( RCF::TcpEndpoint("localhost", 50001) );
c.reverse(v);
}


int main()
{
for(int i=0;i<60;i++)
_beginthread(ThrFoo,0,0);
Sleep(10000);
return 0;
}


the client crashes randomly even each thread has it's own RcfClient object.

Crashes happen

here:
Client.exe!boost::detail::sp_counted_base::release()  Line 100 + 0x9 bytes	C++


here:
Client.exe!std::_Vector_const_iterator"#unknown">"#unknown">,boost::shared_ptr<rcf::bytebuffer,std::allocator>"#unknown"> > > >,std::allocator,boost::shared_ptr<rcf::bytebuffer,std::allocator> > > > > >::_Compat(const std::_Vector_const_iterator,boost::shared_ptr<rcf::bytebuffer,std::allocator> > > >,std::allocator,boost::shared_ptr<rcf::bytebuffer,std::allocator> > > > > > & _Right=({px=0x02019108 pn={...} },{px=[1]({mSpvc={...} mSpos={...} mPv=0x02014e98 "//	MyService" ...}) pn={...} }))  Line 251 + 0x14 bytes	C++


here:
Client.exe!boost::detail::sp_counted_base::add_ref_copy()  Line 73 + 0xb bytes	C++


here:
Client.exe!std::vector"#unknown">"#unknown">,boost::shared_ptr<rcf::bytebuffer,std::allocator>"#unknown"> > > >,std::allocator,boost::shared_ptr<rcf::bytebuffer,std::allocator> > > > > >::_Insert_n(std::_Vector_const_iterator,boost::shared_ptr<rcf::bytebuffer,std::allocator> > > >,std::allocator,boost::shared_ptr<rcf::bytebuffer,std::allocator> > > > > > _Where=({px=0xfeeefeee pn={...} },{px=[...]() pn={...} }), unsigned int _Count=1, const std::pair,boost::shared_ptr<rcf::bytebuffer,std::allocator> > > > & _Val=({px=0x00000000 pn={...} },{px=[...]() pn={...} }))  Line 1163 + 0x14 bytes	C++


I think wherever the shared variables were altered by the threads;

If the call is protected with a critical section the things are working well, but it shouldn't
as long there are separate client instances / thread.

 EnterCriticalSection(&cs);
c.reverse(v);
LeaveCriticalSection(&cs);


It make sense to protect them if the threads are working on the same instance...


void ThrFoo(void* pv)
{
RcfClient* pService = reinterpret_cast* >pv;
//...omitted for clarity
EnterCriticalSection(&cs);
pService->reverse(v);
LeaveCriticalSection(&cs);
}


int main()
{
InitializeCriticalSection(&cs);
RcfClient c( RCF::TcpEndpoint("localhost", 50001) );
for(int i=0;i<60;i++)
_beginthread(ThrFoo,0,reinterpret_cast(&c));
Sleep(10000);
DeleteCriticalSection(&cs);
return 0;
}


Marius C.

GeneralRe: Concurent server call
Jarl Lindrud
23:15 1 Oct '08  
Which version of RCF are you using? The link at the top of this page is quite old. Have you tried your code against RCF 0.9d?

You can get it here:
http://code.google.com/p/rcf-cpp/downloads/list

There is a newer CodeProject article as well:
http://www.codeproject.com/KB/threads/Rcf_Ipc_For_Cpp.aspx

Regards,
Jarl.
Generalwhat does RCF stand for ?
josh552
0:23 18 Jun '08  
what does RCF stand for ? Smile
GeneralRe: what does RCF stand for ?
Jarl Lindrud
0:34 18 Jun '08  
Remote Call Framework.... There's an updated article here:

http://www.codeproject.com/KB/threads/Rcf_Ipc_For_Cpp.aspx
GeneralInteroperability with other languages.
Vess Bakalov
17:25 27 Apr '08  
This looks like a great library.

I am wondering if compatible implementations exist in Perl or PHP or Python or any other popular scripting language?

I am looking for something that would give me the interoperability of SOAP, without the performance penalty.

To give you a bit more details - I am looking for a framework to use for an open API project, but I do not want to have to re-implement the API for every single target language. SOAP gives us this flexibility. However in certain cases high volumes of messages need to be exchanged, and in those cases SOAP becomes a major performance bottleneck.

I am sorry if this has already been answered, but I did search the forum as best as I could.

Thanks!

vess

P.S. If I am looking at the wrong tool, and there are better suggestions, please let me know as well.
GeneralRe: Interoperability with other languages.
Jarl Lindrud
1:34 29 Apr '08  
Hi,

RCF is implemented in C++ only, so from other languages you would need to write a wrapper for a C++ layer that handles the network communication.

Regards,
Jarl.
QuestionIPC probs/speed
Member 3873632
4:37 13 Mar '08  
Hi All

I'm novice on this forum, sorry for possible confusions.

My task is to port a 3d render engine from 32-bits to 64. There are a lot of things are (at least) difficult to port (GUI, QuickTime calls, plug-ins, shaders etc). Thus I plan to port "core" to 64 and leave other parts in 32. The only way to do this is to have 2 processes (one 32-bits, another one 64) and organize their interactions. So my questions are:

1) Can I use RCF for OSX (Carbon) platform?
2) Can I use RCF for Windows platform?
3) What is a fastest IPC (no probs if it's low-level/primitive, speed is critical for me)?

Thank you

Igor
GeneralRe: IPC probs/speed
Jarl Lindrud
1:27 14 Mar '08  
Hi Igor,

First of all, this article refers to an old version of RCF... The new one is here:

http://www.codeproject.com/KB/threads/Rcf_Ipc_For_Cpp.aspx

, and the project homepage is here:

http://code.google.com/p/rcf-cpp/

RCF works well as a 32/64 bit bridge, I've seen it used for that purpose myself. As for your questions,

1) Probably, with some minor tweaking. I don't have access to an OSX platform to test on, but I know users who have made small changes to RCF, and been able to run it on OSX.

2) Yes.

3) In RCF 0.9c you would be best off using TCP. UDP is faster, but doesn't offer any form of connection-oriented interaction, which is often a deal-breaker.

RCF will, probably in the next release, support named pipe transports on Windows, which will be a bit faster than TCP. TCP on a localhost connection is quite fast though, most applications would not have a problem with it.

Regards,
Jarl.
QuestionAsync features
hulkaspeed
1:31 19 Nov '07  
Hi Jarl,
First,only tell you that I think this is one of the best and more usable code ever written!!
Now, I only have one question,how can I develop an asynchronous call?could you give me an example??

Thanks

Hulka
AnswerRe: Async features
Jarl Lindrud
15:01 19 Nov '07  

Thanks hulka. By the way, there are much newer versions of RCF, have a look here:

http://www.codeproject.com/threads/Rcf_Ipc_For_Cpp.asp

Asynchronous calls are not yet built in to RCF, but I am working on that right now... The next release of RCF will have native asynchronous capability, but until then, if you need a thread to do something while a call is pending, you can either use client progress callbacks, or start your own background threads, and queue up remote calls on that while the main thread does something else.

In RCF 0.9c, have a look in Test_ClientProgress.cpp to see how client progress callbacks are used.

Cheers,
jarl.

QuestionHow to use array in argument?
ykishii_hitachi
20:25 10 Jul '07  
Hello Jarl,

I’m trying to use your RCF. Start of all, I started the next interface.
RCF_METHOD_V1(void, func1, std::vector)
This test works well.
OS : Redhat Linux WS4
Machine : IBM Thinkpad X41
Development Environment: Eclipse 3.2 with CDT
using NO_BOOST_SERIALIZATION option.

In the next case, I started to build next 2 interface.

(1) 1’st case -- Static Array Version
typedef int intarray[100];
:
RCF_METHOD_V1(void, func1, intarray)

In this case , I can compile source code, but in runtime, I encountered the next runtime error in client side.
[Error Message]
Caught exception;
Type: N3RCF18RecvCloseExceptionE
What: ../../RCF/include/RCF/Marshal.inl(77); RCF::IDL::InReturnValue ::InReturnValue(RCF::Connection&,ool): :Thread-id=0 : Timestamp(ms)=51465:THROW:RCF:RecvCloseException: connection closed by server

(2) 2’nd case -- Dinamic Array Version
typedef SF::DynamicArray IntArray;
:
RCF_METHOD_V1(void, func1, IntArray)

In this case, compiler generates error! In Compile process, code “RCF_METHOED_V1(void, func1, IntArray)” is perhaps converted to a code to instantiate “SF::DynamicArray” with no argument. but in SF/SerializeDynamicArray.hpp, no default creator is defined. So error is generated(I guess from complier error message).

Please tell me how can I use Array in RCF argument.

Best regards
ykishii
AnswerRe: How to use array in argument?
Jarl Lindrud
21:16 10 Jul '07  

First of all, you need to use a newer version of RCF:

http://www.codeproject.com/threads/Rcf_Ipc_For_Cpp.asp

To send an array of integers, you should use std::vector as the type of the parameter. Static arrays like int[] are not supported as interface parameters. SF::DynamicArray is an internal implementation class, and is not intended for use as an RCF interface parameter, either.

Regards,
Jarl.

GeneralRe: How to use array in argument?
ykishii_hitachi
0:08 11 Jul '07  
Thank you, Mr. Jarl,

RCF_METHOD_V1(void, func1, std::vector<int>)
This test works well. The next data present the elapsed time for remote call, this shows that remote call requires the time linear to the data length.

Size of vector elapsed time(average of 1000 calls)
10 0.00164335[s/call]
100 0.00270757[s/call]
1000 0.0109117[s/call]
10000 0.0927689[s/call]
100000 0.931833[s/call]

I want to speed up remote method call, and I think that Array may be sent more quickly.
Former question is to confirm this hypothesis, but not supported by RCF.

Is there any advice to shorten the time required to the remote call(with large vector data)?

Regards,
ykishii.
GeneralRe: How to use array in argument?
Jarl Lindrud
22:42 25 Jul '07  
Hi ykishii,

Sorry for the late response... If you get the latest version of RCF, RCF 0.9c, available here , you can use the RCF::ByteBuffer class as a parameter in your interfaces, and that should be a lot faster than using std::vector with older versions of RCF.

Regards,
jarl.
QuestionRCF::RecvTimeoutException
Petr48
23:14 22 Jun '06  
I would need to solve this problem:
If some remote function does not respond to the client RCF::RecvTimeoutException is thrown.
But how to kill a remote function on the server which caused this exception.
Could you help me ?
Thanks,
Petr

http://www.codeproject.com/script/profile/confirm.asp?i=3129526&h=3B74C57CC521D8900F45

AnswerRe: RCF::RecvTimeoutException
Jarl Lindrud
1:24 23 Jun '06  

Hi,

It sounds like you want to explicitly kill a server thread, which is not really a good idea. In general there's only 1 thread in the server, answering client requests one at a time, so if you kill it, the server will no longer respond to any client requests.

If you're bent on doing it, though, the operating system API has functions for killing threads, any of those would do.

BTW, there is a new and improved version of this framework, here.[^]

Regards,
Jarl.

Generalboost dependency
Hua Zhong
13:05 26 Apr '06  
Hi Jarl,

I was looking for a lightweight RPC library for C++ and found your page. This looks like a very nice interface and attracted me immediately.

However, it seems to rely on boost 1.33.0. I have two questions:

1. Are you planning to make it independent of boost? How much effort would that be? (I could probably help a bit if it's not too much of work)

2. Or are you planning to make it compatible with boost 1.33.1 (or whatever the latest version is)? I tried to build it with 1.33.1 and it complains about missing files (test/minimal.hpp, read_write_mutext.hpp, etc)

Thanks.

Hua
GeneralRe: boost dependency
Jarl Lindrud
3:02 27 Apr '06  

Hi Hua,

Sorry, I didn't realize there were any problems compiling with boost 1.33.1, I'll put together an update and post it here. The code is meant to work with any of the newer versions of boost.

As for making RCF independent of Boost, that has so far not been a high priority for me Smile I've used Boost pretty extensively thoughout RCF, and eliminating it would be a lot of work, with questionable results, IMHO.

Finally, I should point out that I'm no longer actively maintaining this version of RCF; there is a newer version available here:

http://www.codeproject.com/threads/Rcf_Ipc_For_Cpp.asp[^]

It has all the features of this version, with a lot of extra things as well (encryption, compression, publish/susbcribe, interface inheritance, etc.)

Regards,

Jarl.

GeneralThanks
mattavila
7:56 13 Mar '06  
Jarl,

I just wanted to put in a good word for your project. I am using the C++ RMI framework for a project and found the overall setup very nice. We are using the framework to glue a Matlab application to binary data base. Writing/modification of the Matlab client ".mex" was the most challenging part. We did have to up the maximum message size from the original 1024*50 to support our needs.

Cheers,
Matt
GeneralRe: Thanks
Jarl Lindrud
1:42 15 Mar '06  

Good to hear, thanks for the info!

/Jarl.


Last Updated 6 Aug 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010