基礎服務器
咱們從一個標準的echo服務器和客戶端的例子來開始,這樣的例子能夠在幾乎全部的網絡和IPC示例中見到。咱們暴露(expose)而後調用一個函數,這個函數接受一個字符串,而且返回一個相同的字符串,使用RCF,服務器端的代碼是這樣的:網絡
#include RCFIdl.hpp #include RCFRcfServer.hpp #include RCFTcpEndpoint.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 &s) { return s; } }; int main() { Echo echo; RCF::RcfServer server(RCF::TcpEndpoint(50001)); server.bind<I_Echo>(echo); server.startInThisThread(); return 0; }
... 客戶端代碼以下:多線程
#include <RCF/Idl.hpp> #include <RCF/TcpEndpoint.hpp> RCF_BEGIN(I_Echo, "I_Echo") RCF_METHOD_R1(std::string, echo, const std::string &) RCF_END(I_Echo) int main() { RcfClient<I_Echo> echoClient(RCF::TcpEndpoint("localhost", 50001)); std::string s = echoClient.echo(RCF::Twoway, "what's up"); return 0; }
I_Echo是用RCF_BEGIN/RCF_METHOD/RCF_END宏進行定義的一個接口。這些接口對應於CORBA裏的IDL定義,可是在這裏,接口定義是被放在C++的源代碼裏,因此並不須要另外的編譯步驟。服務器端和客戶端代碼簡單地包含這些接口,而後和其餘的代碼一塊兒編譯。app
客戶端樁(stub)中的參數RCF::Twoway用來告訴RCF採用一個two-way方式的客戶端調用,這種方式下,客戶端發送一個請求而且等待響應,若是在必定時間內沒有收到響應(這個時間是可配的),將會拋出一個異常。另外一個選項是RCF::Oneway,這種方式下,若是服務端沒有發送響應,客戶端調用樁會當即把控制返回給用戶。函數
在客戶端調用中,把這個雙向實參(directional argument)做爲第一個參數,能夠在代碼裏清楚地知道遠程調用是採用哪一種方式。通常說來,遠程調用都會被規劃爲看起來像本地調用同樣(正統的RPC觀點),但在我看來,透明更重要一些。然而,若是你願意,經過不傳遞那個雙向實參(directional argument)從而以RPC風格來進行一個遠程調用(這時調用是以two-way方式進行的)。ui
客戶端樁時沒有進行任何同步操做,因此應該在同一個線程裏進行訪問。服務端儘管是支持多線程,但在上面的例子中,仍是以一個線程來運行的。RcfServer::startInThisThread()劫持了調用線程,並把它變爲一個工做線程。this
能夠經過調用RcfServer::start(
false)
而後重複調用RcfServer::cycle()
來達到相同的效果。在多線程版本中,也能夠調用RcfServer::start()
,而後驅動服務器的現場將會被自動建立。多線程版本須要Boost.Threads
庫,而且要定義RCF_USE_BOOST_THREADS
預處理符號。spa
咱們也能夠用UDP協議來重寫上面的客戶端和服務器端。此次咱們讓服務器端和客戶端程序運行在一個進程的不一樣線程裏。線程
#include <RCF/Idl.hpp> #include <RCF/RcfServer.hpp> #include <RCF/UdpEndpoint.hpp> #ifndef RCF_USE_BOOST_THREADS #error Need to build with RCF_USE_BOOST_THREADS #endif 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 &s) { return s; } }; int main() { Echo echo; RCF::RcfServer server(RCF::UdpEndpoint(50001)); server.bind<I_Echo>(echo); server.start(); RcfClient<I_Echo> echoClient(RCF::UdpEndpoint("127.0.0.1", 50001)); std::string s = echoClient.echo(RCF::Twoway, "what's up"); server.stop(); // would happen anyway as server object goes out of scope return 0; }
和TCP不一樣的是UDP是無狀態的。數據包不能保證是發送時的順序被接收到,也不能保證全部的發送的數據包都會被接收到。在像上面的例子的本地鏈接中,使用two-way方式通常來講是可以正常工做的,由於這些數據包並無從變幻莫測的真實網絡中傳輸。一般,這種狀況應該用one-way方式。code
接口
接口定義宏的功能和以前一個版本的RCF是徹底一致的。RCF_BEGIN()
宏用給定的名字和運行時描述來開始一個接口的定義,RCF_END()
宏來結束接口定義。在二者中間,用RCF_METHOD_xx()
宏能夠定義一共25個接口的成員方法。
RCF_METHOD_xx()
宏的後兩個字母表示參數的個數和返回類型是不是一個void。好比說,RCF_METHOD_V3
用來定義一個三個參數返回值類型爲void的方法。
使用這些宏定義了RcfClient
<type
>類,這裏的type
是這個接口的名字。這個類直接用做客戶端的客戶端樁,間接地用做服務器端的服務樁。RCF的接口也能夠定義在任意的名字空間內:
namespace A { namespace B { RCF_BEGIN(I_X, "I_X") RCF_METHOD_V0(void, func1) RCF_METHOD_R5(int, func2, int, int, int, int, int) RCF_METHOD_R0(std::auto_ptr<std::string>, func3) RCF_METHOD_V2(void, func4, const boost::shared_ptr<std::string> &, boost::shared_ptr<std::string> &) // .. RCF_END(I_X) } } int main() { A::B::RcfClient<A::B::I_X> client; // or A::B::I_X::RcfClient client; // ... }
服務器綁定
在服務器端,接口須要綁定到一個具體的實現上。經過RcfServer::bind()
方法來實現這個綁定。根據內存管理方式的不一樣,綁定有幾種變化。下面的幾種調用均可以實現如出一轍的事情:把一個Echo
對象綁定到I_Echo
接口上。
{ // bind to an object... Echo echo; server.bind<I_Echo>(echo); // or to a std::auto_ptr<>... std::auto_ptr<Echo> echoAutoPtr(new Echo()); server.bind<I_Echo>(echoAutoPtr); // or to a boost::shared_ptr<>... boost::shared_ptr<Echo> echoPtr(new Echo()); server.bind<I_Echo>(echoPtr); // or to a boost::weak_ptr<>... boost::weak_ptr<Echo> echoWeakPtr(echoPtr); server.bind<I_Echo>(echoWeakPtr); }
默認狀況下,客戶端能夠經過接口的名字來使用綁定。服務器端能夠經過同一個接口暴露(expose)多個服務器端對象,可是在這種狀況下,須要明確指定每一個對象的名字:
{ RcfServer server(endpoint); // bind first object Echo echo1; server.bind<I_Echo>(echo1, "Echo1"); // bind second object Echo echo2; server.bind<I_Echo>(echo2, "Echo2"); server.start(); RcfClient<I_Echo> echoClient(endpoint); echoClient.getClientStub().setServerBindingName("Echo1"); std::cout << echoClient.echo("this was echoed by the echo1 object"); echoClient.getClientStub().setServerBindingName("Echo2"); std::cout << echoClient.echo("this was echoed by the echo2 object"); }