You need a cross-language interprocess framework that is hard to setup, learn and deploy, just for the thrill of being able to use Java and/or PHP on the server side while using native code on the client side?
Then this is not for you. Please skip this post.
For developers using the most powerful language - in terms of its combination of runtime efficiency and expressivity - i.e., C++, both on client and servers, there is finally an interprocess framework for you. You are probably working on the next P2P kill app, right? Continue reading!
RCF is a C++-only library making the connection to a server node, and subsequent marshalling of data, almost transparent. The author, Jarl Lindrud, sometimes refers to it as "RMI for C++." This is not true, since RCF is so much better than RMI. Believe me, I have struggled with, and extended, RMI for years.
One interesting side note about RCF is that Jarl decided not to promote the building of proper lib files - with the intricacies of compiling with the right thread support and so forth - but instead urge developers to simply include one file, RCF.cpp, into the project. That is simple "linking."
RCF uses C++ fully, i.e., it does not shy away from being as generic as possible, via heavy template use, and is somewhat agnostic as to the underlying I/O sub system and serialization facilities, even though it currently supports Boost.Asio (not an offical part of current Boost release) for I/O and Boost.Serialization for serialization of C++ objects. Those two libraries deserve a review of their own. Later.
Where is the Hello World!? Easy! We are almost there. Before that, I will introduce to RCF interfaces, which seems to be influenced by both MFC and the language D.
Imagine we want to have a powerful Greeting interface between nodes, with one method greetMe that returns a proper greeting to a client. It could look like
-
RCF_BEGIN(I_Greeting, "I_Greeting")
-
RCF_METHOD_R1(std::string, greetMe, const std::string&)
-
RCF_END(I_Greeting)
So, the greetMe method expects a string - which is the name of the client - and returns a string - the greeting. No strange types, or awkward string types, but the native types of C++ (which is a language that does come with a Standard Library, which certain providers forget, including Microsoft and RogueWave.) Ah, and no IDL!
Now when the interface definition was this simpe, the server implementation must be really hard, to counter-balance. Righ? Wrong!
The server implementation follows.
-
class Greeting {
-
public:
-
std::string greetMe(const std::string& name) {
-
return "Greetings, " + name +
-
", do you want to play a game?";
-
};
Wait, there is no mentioning of I_Greeting. Surely I have to inherit, or whatever it is called, some class? Nope. As long as the class has the proper syntactical methods that are part of the RCF interface, you are in good shape.
Now when we have the interface and an implementation of it, we want to associate the two of them and start listening on a port. That constitutes the wholy trinity of remoting: (i) interface, (ii) implementation and (iii) communication port. RCF actually supports both UDP and TCP, and we here choose to listen for UDP packets on a port. The binding code looks like:
-
Greeting greeting;
-
RCF::RcfServer server(RCF::UdpEndpoint(serverPort));
-
server.bind<I_Greeting>(greeting);
-
server.start();
Note that lines 1-3 directly correpond to that wholy trinity while line 4 actually starts the "server thread." RCF have chosen to implement its own threading mini-library, but you can use Boost.Thread (I think... have not tried it myself, but having a certain feel for Jarl's generic mentality, I think so
)
In order to make use of this service transparent, we need a proxy, looking like a regular object. This is done via
-
RcfClient<I_Greeting> greetingProxy(
-
RCF::UdpEndpoint(serverAddress, serverPort));
After this, we can use it in a client:
-
std::string greeting = greetingProxy.greetMe(
-
RCF::Twoway, "David");
-
std::cout <<"He said: " <<greeting;
which produces the output
He said: Hello, David, do you want to play a game?
The Twoway "specifier" indicates that we want to have the server acknowledging the packet(s) orderly reception, which is necessary in most UDP scenarios, since UDP is stateless.
I should talk about how not only the standard containers and types are supported, transparently, but how you can easily extend the serialization to user-defined types via Boost.Serialization mechanisms, but that is slightly outside the scope of this post.
I end with a complete example, starting both server and client.
/* File: test_rcf.cpp Author: David Bergman Testing Jarl Lindrud's RCF library */ #include <iostream> #include <RCF/Idl.hpp> #include <RCF/RcfServer.hpp> #include <RCF/UdpEndpoint.hpp> RCF_BEGIN(I_Greeting, "I_Greeting") RCF_METHOD_R1(std::string, greetMe, const std::string &) RCF_END(I_Greeting) class Greeting { public: std::string greetMe(const std::string &name) { return "Hello, " + name + ", do you want to play a game?"; } }; int main() { // Server Greeting greetingImpl; RCF::RcfServer server(RCF::UdpEndpoint(9001)); server.bind<I_Greeting>(greetingImpl); server.start(); // Client RcfClient<I_Greeting> greetingProxy( RCF::UdpEndpoint("localhost", 9001)); std::string greeting = greetingProxy.greetMe(RCF::Twoway, "David"); std::cout << "He said: " << greeting; }
Download this code: test_rcf.cpp
C++ ipc p2p rcf rpc templates Tools Reviews