RCF—用於C++的進程間通信(1)

基礎服務器

咱們從一個標準的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");
}
相關文章
相關標籤/搜索