介绍
RCF(远程调用框架)是一个C ++ IPC框架,提供了一种在C ++程序中实现进程间通信的简单而一致的方法。它基于强类型的客户端/服务器接口的概念,这是基于IDL的中间件(如CORBA和DCOM)的用户熟悉的概念。然而,通过仅适用于C ++,RCF可以利用C ++特定的语言特性,允许以更简单和更少的混乱方式来制定进程间调用。
RCF通过支持压缩(zlib)和多个传输(TCP,UDP,Windows命名管道,UNIX本地域套接字)提供了大量IPC消息传递范例(单独批量,单独批量,请求/响应,发布/订阅)加密和认证技术的数量(Kerberos,NTLM,Schannel,OpenSSL)。您可以在各种IPC方案中使用RCF,从简单的父子进程通信到大规模分布式系统。最重要的是,RCF是100%标准的C ++,并且将在任何具有C ++编译器的平台上运行。
RCF 网站上提供RCF版本和文档。本文简要介绍了RCF。
基本
我们将从一个简单的回显服务器和客户端开始。我们希望服务器向客户端公开接受字符串的函数,并返回相同的字符串。使用RCF,服务器代码变为:
#include <RCF/Idl.hpp>
#include <RCF/RcfServer.hpp>
#include <RCF/TcpEndpoint.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
宏定义的RCF接口。这些宏对应于CORBA中的IDL定义,但是在这种情况下,接口定义被放置在C ++源代码中,并且不需要单独的编译步骤,就像IDL接口一样。该接口只包含在服务器和客户端的源代码中,并与程序的其余部分一起编译。
RCF::Twoway
客户端存根调用中的参数是一个标志,告诉RCF进行双向客户端调用; 客户端发送请求,等待响应,如果在可配置的持续时间内没有收到请求,则会抛出异常。另一个选择是使用RCF::Oneway
; 发送请求,但服务器不发送响应,客户端存根调用将立即将控制权返回给用户。如果省略了方向参数,RCF::Twoway
则默认使用。
客户端存根不以任何方式同步,并且一次只能由单个线程访问。但是,服务器本质上是多线程的,尽管在上面的示例中,它已经被写为单线程进程。RcfServer::startInThisThread()
劫持调用线程并将其转换为服务器的工作线程。
我们也可以调用RcfServer::start()
,然后自动创建驱动服务器所需的线程。RcfServer::start()
启动服务器线程后立即返回。如果我们这样做,我们需要为主线程设置一个等待循环,以便它不会离开main()
,关闭整个进程。
我们可以重写我们的客户端/服务器对来改用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;
}
UDP当然与TCP显着不同。TCP提供可靠的有序传送的网络数据包,而UDP根本不保证发送的数据包的顺序或数据包是否甚至到达。在一个环回连接上,就像上面的例子中一样,由于数据包不会受到真实网络的变幻莫测,所以可以通过UDP使用双向语义来避免。但是,一般来说,您只想使用UDP发送单向呼叫。
接口
界面定义宏的功能与上一代RCF 完全一样。的RCF_BEGIN()
宏开始具有给定名称和运行时描述的接口定义,并且RCF_END()
结束该接口定义。在它们之间,最多可以有25个RCF_METHOD_xx()
宏来定义接口的成员方法。
RCF_METHOD_xx()
宏中的最后两个字母表示参数的数量以及返回类型是否为void
。例如,RCF_METHOD_V3
用于定义具有三个参数和void
返回值RCF_METHOD_R2
的方法,同时定义了一个带有两个参数的方法和一个非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);
}
默认情况下,该绑定可通过接口名称向客户端发送。服务器还可以通过同一个接口暴露几个对象,但在这种情况下,它需要显式设置绑定名称:
{
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");
}
整编
RCF遵循C ++约定,当涉及到确定哪些方向参数被封送时。特别地,接口方法的所有参数都是in
参数,所有非常量引用参数都是inout
参数,返回值是一个out
参数。目前,没有任何规定在IDL定义的in
/ out
/ inout
限定符中重写这些约定。
不是C ++中的所有内容都可以安全地封送,这对接口方法的参数类型有一些限制。相反,指针和引用被允许作为参数; 不允许对指针的引用作为参数; 指针和引用不允许作为返回值。
这意味着如果一个接口方法需要返回一个指针 - 例如一个多态指针 - 那么返回类型需要是一个智能指针,例如std::auto_ptr<>
或boost::shared_ptr<>
。或者,其中一个参数可能是对智能指针的非常量引用。
序列化
echo示例只是序列化一个std::string
对象,但是可以在使用的序列化系统的限制内几乎发送任何C ++类或结构。RCF有自己的序列化框架,简称为序列化框架(SF),但它也支持Boost.Serialization框架,Boost库的一部分。
一般来说,有必要为被封送的类包含序列化代码。如果std::vector<>
接口中有参数,则需要包含<SF / vector.hpp>或<boost / serialization / vector.hpp>(或两者),对于其他STL和Boost类,也需要包含参数。
要在RCF接口中使用您自己的类作为参数,您需要定义自定义序列化函数。在大多数情况下,这很简单:
#include <boost/serialization/string.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <SF/string.hpp>
#include <SF/map.hpp>
#include <SF/vector.hpp>
struct X
{
int myInteger;
std::string myString;
std::map<
std::string,
std::map<int,std::vector<int> > > myMap;
};
// This serialization function will work as is, with both SF and B.S.
template<typename Archive>
void serialize(Archive &ar, X &x)
{
ar & x.myInteger & x.myString & x.myMap;
}
// If you need to distinguish between SF and B.S. serialization,
// specialize the SF serialization function:
//void serialize(SF::Archive &ar, X &x)
//{
// ar & myInteger & myString & myMap;
//}
序列化可以很快变成一个复杂的事情,特别是在处理多态对象指针和指针周期时。SF和Boost.Serialization都处理这些情况,但是以不同的方式,并且对于两者都可能需要为每个序列化系统编写单独的序列化代码。以下是使用SF和Boost.Serialization两者发送多态对象的示例:
class Base
{
// Some members here.
// ...
};
typedef boost::shared_ptr<Base> BasePtr;
class Derived1 : public Base
{
// Some members here.
// ...
};
class Derived2 : public Base
{
// Some members here.
// ...
};
template<typename Archive>
void serialize(Archive &ar, Base &base, const unsigned int)
{
// ...
}
template<typename Archive>
void serialize(Archive &ar, Derived1 &derived1, const unsigned int)
{
// Valid for both SF and B.S.
serializeParent<Base>(derived1);
// ...
}
template<typename Archive>
void serialize(Archive &ar, Derived2 &derived2, const unsigned int)
{
// Valid for both SF and B.S.
serializeParent<Base>(derived1);
// ...
}
// Boost type registration, needed on both server and client.
BOOST_CLASS_EXPORT_GUID(Derived1, "Derived1")
BOOST_CLASS_EXPORT_GUID(Derived2, "Derived2")
RCF_BEGIN(I_PolymorphicArgTest, "")
RCF_METHOD_R1(std::string, typeOf, BasePtr)
RCF_END(I_PolymorphicArgTest)
class PolymorphicArgTest
{
public:
std::string typeOf(BasePtr basePtr)
{
return typeid(*basePtr).name();
}
};
{
// SF type registration, needed on both server and client.
SF::registerType<Derived1>("Derived1");
SF::registerType<Derived2>("Derived2");
RcfClient<I_PolymorphicArgTest> client(endpoint);
// SF serialization (default).
client.getClientStub().setSerializationProtocol(RCF::SfBinary);
std::string typeBase = client.typeOf( BasePtr(new Base()) );
std::string typeDerived1 = client.typeOf( BasePtr(new Derived1()) );
std::string typeDerived2 = client.typeOf( BasePtr(new Derived2()) );
// Boost serialization.
client.getClientStub().setSerializationProtocol(RCF::BsBinary);
typeDerived2 = client.typeOf( BasePtr(new Derived2()) );
}
遗产
RCF接口现在支持多重继承。接口不仅可以互相继承,还可以从标准的C ++类继承。接口中的方法通过它们的分派ID和它们所属的接口的名称的组合来标识。此信息足以使服务器将传入的客户端调用映射到服务器绑定上的正确功能。下面的例子演示了接口继承:
RCF_BEGIN(I_A, "I_A")
RCF_METHOD_V0(void, func1)
RCF_END(I_Base)
RCF_BEGIN(I_B, "I_B")
RCF_METHOD_V0(void, func2)
RCF_END(I_Base)
// Derives from I_A.
RCF_BEGIN_INHERITED(I_C, "I_C", I_A)
RCF_METHOD_V0(void, func3)
RCF_END(I_Base)
// Derives from I_A and I_B.
RCF_BEGIN_INHERITED_2(I_D, "I_D", I_A, I_B)
RCF_METHOD_V0(void, func4)
RCF_END(I_Base)
// Derives from abstract base class I_E.
RCF_BEGIN_INHERITED(I_F, "I_F", I_E)
RCF_METHOD_V0(void, func5)
RCF_END(I_Base)
{
RcfClient<I_C> clientC(endpoint);
clientC.func3();
clientC.func1();
RcfClient<I_D> clientD(endpoint);
clientD.func4();
clientD.func2();
clientD.func1();
}
过滤器
RCF支持通过一个过滤器概念来开箱即用的消息的压缩和加密。过滤器应用于服务器端和客户端,并且可以应用于传输层 - 例如,将SSL过滤器应用于像TCP这样的面向流的传输,或应用于单个消息有效载荷,例如压缩发送到面向分组的传输(如UDP)的消息。第一种情况我们称之为传输过滤器,第二种情况是一个消息过滤器。
运输过滤器
在服务器 - 客户端会话上安装传输过滤器的过程由客户端发起。客户端查询服务器以确定服务器是否支持给定的过滤器。如果服务器执行,则过滤器安装在传输的两端,并且通信恢复。
查询服务器和安装过滤器的过程由客户端存根处理。客户端调用该ClientStub::requestTransportFilters()
功能,如果与服务器协商成功,则过滤器序列然后处于活动状态,并应用于所有后续远程调用。
传输过滤器可以是双向的,这意味着在过滤器上的单个读取或写入请求可能导致在连接上进行多个读取和写入请求。例如,通过加密过滤器发送的第一个消息将启动涉及多个下游读取和写入操作的某种握手。
邮件过滤器
消息过滤器不需要代表服务器或客户端进行任何协商。如果客户端存根通过一系列消息过滤器ClientStub::setMessageFilters()
,则消息将通过给定的过滤器传递。编码消息将以描述已使用哪些过滤器的数据为前缀,从而允许服务器对消息进行解码。如果服务器无法识别过滤器,则会将异常传回客户端。
RCF自带的几个过滤器:两个用于压缩,基于Zlib,两个用于基于OpenSSL和Schannel的 SSL加密,以及两个基于Windows的Kerberos和NTLM协议过滤器。这些过滤器也可以相互链接以创建过滤器序列。
加密过滤器只能用作传输过滤器,因为加密和解密过程需要双向握手。压缩过滤器可以用作传输或消息过滤器,但是通过非流传输的传输(如UDP),仅使用无状态压缩过滤器才有意义。
一个例子:
{
// Compression of payload.
RcfClient<I_X> client(endpoint);
client.getClientStub().setMessageFilters( RCF::FilterPtr(
new RCF::ZlibStatelessCompressionFilter() ) );
// Encryption of transport.
std::string certFile = "client.pem";
std::string certFilePwd = "client_password";
client.getClientStub().requestTransportFilters( RCF::FilterPtr(
new RCF::OpenSslEncryptionFilter(certFile, certFilePwd)) ) );
// Multiple chained transport filters - compression followed by encryption.
std::vector<RCF::FilterPtr> transportFilters;
transportFilters.push_back( RCF::FilterPtr(
new RCF::ZlibStatefulCompressionFilter()));
transportFilters.push_back( RCF::FilterPtr(
new RCF::OpenSslEncryptionFilter(certFile, certFilePwd)) ) );
client.getClientStub().requestTransportFilters(transportFilters);
// Multiple chained payload filters - double compression.
std::vector<RCF::FilterPtr> payloadFilters;
payloadFilters.push_back( RCF::FilterPtr(
new RCF::ZlibStatefulCompressionFilter()));
payloadFilters.push_back( RCF::FilterPtr(
new RCF::ZlibStatefulCompressionFilter()));
client.getClientStub().setMessageFilters(payloadFilters);
}
{
std::string certFile = "server.pem";
std::string certFilePwd = "server_password";
// FilterService service enables the server to load the filters it needs.
RCF::FilterServicePtr filterServicePtr( new RCF::FilterService );
filterServicePtr->addFilterFactory( RCF::FilterFactoryPtr(
new RCF::ZlibStatelessCompressionFilterFactory) );
filterServicePtr->addFilterFactory( RCF::FilterFactoryPtr(
new RCF::ZlibStatefulCompressionFilterFactory) );
filterServicePtr->addFilterFactory( RCF::FilterFactoryPtr(
new RCF::OpenSslEncryptionFilterFactory(certFile, certFilePwd)) );
RCF::RcfServer server(endpoint);
server.addService(filterServicePtr);
server.start();
}
远程对象
RcfServer
该类允许用户将类的单个实例公开到远程客户端,但是它不会为远程客户端在服务器上创建任何对象提供任何条件。这个功能是ObjectFactoryService
服务的一部分。一旦将ObjectFactoryService
服务安装到服务器中,客户端可以通过该ClientStub::createRemoteObject()
功能创建与服务配置为允许的尽可能多的对象。
该ObjectFactoryService
服务实现垃圾收集策略,从而最终删除不再使用的对象(即,没有活动的客户端会话,并且在可配置的持续时间内未被访问)。
{
// Allow max 50 objects to be created.
unsigned int numberOfTokens = 50;
// Delete objects after 60 s, when no clients are connected to them.
unsigned int objectTimeoutS = 60;
// Create object factory service.
RCF::ObjectFactoryServicePtr ofsPtr(
new RCF::ObjectFactoryService(numberOfTokens, objectTimeoutS) );
// Allow clients to create and access Echo objects, through I_Echo.
ofsPtr->bind<I_Echo, Echo>();
RCF::RcfServer server(endpoint);
server.addService(ofsPtr);
server.start();
}
{
RcfClient<I_Echo> client1(endpoint);
bool ok = client1.getClientStub().createRemoteObject();
// client1 can now be used to make calls
// to a newly created object on the server.
RcfClient<I_Echo> client2(endpoint);
client2.getClientStub().setToken( client1.getClientStub().getToken() );
// client1 and client2 will now be making calls to the same object
}
会话对象
创建的对象由ObjectFactoryService
令牌标识,并且可以由指定该特定令牌的任何客户端访问。所以一旦创建,这样一个对象可能被许多客户端访问。但是,客户端想要创建只能由特定客户端访问的对象是很常见的。在RCF中,这样的对象称为会话对象,由SessionObjectFactoryService
以下方式创建:
{
// Create session object factory service.
RCF::SessionObjectFactoryServicePtr sofsPtr(
new RCF::SessionObjectFactoryService() );
// Allow clients to create and access Echo objects, through I_Echo.
sofsPtr->bind<I_Echo, Echo>();
RCF::RcfServer server(endpoint);
server.addService(sofsPtr);
server.start();
}
{
RcfClient<I_Echo> client1(endpoint);
bool ok = client1.getClientStub().createRemoteSessionObject();
// client1 can now make calls to its
// own private Echo instance on the server.
RcfClient<I_Echo> client2(endpoint);
ok = client2.getClientStub().createRemoteSessionObject();
// client1 and client2 will now be making calls
// to their own session specific Echo instances.
// When the clients close their connections, the server
// will immediately delete the associated session objects.
// ...
}
发布/订阅
发布/订阅模式是众所周知的分布式编程模式。一个进程起着发布者的作用,并将定期的信息包发送给一组订阅者。订阅者呼叫发布商,并请求订阅发布商发布的数据。
对于诸如UDP的分组转发传输,在现有的RCF原语之上构建该功能是比较直接的。对于面向流的传输,特别是TCP,RCF提供了一些额外的功能来实现发布/订阅功能。用户呼叫的连接被反转,然后被发布商用于发布。
这个功能被双重PublishingService
和SubscriptionService
服务所包含。以下示例说明如何使用这些服务:
RCF_BEGIN(I_Notify, "I_Notify")
RCF_METHOD_V1(void, func1, int)
RCF_METHOD_V2(void, func2, std::string, std::string)
RCF_END(I_Notify)
{
RCF::RcfServer publishingServer(endpoint);
RCF::PublishingServicePtr publishingServicePtr(
new RCF::PublishingService() );
publishingServer.addService(publishingServicePtr);
publishingServer.start();
// Start accepting subscription requests for I_Notify.
publishingServicePtr->beginPublish<I_Notify>();
// Call func1() on all subscribers.
publishingServicePtr->publish<I_Notify>().func1(1);
// Call func2(std::string, std::string) on all subscribers.
publishingServicePtr->publish<I_Notify>().func2("one", "two");
// Stop publishing I_Notify and disconnect all subscribers.
publishingServicePtr->endPublish<I_Notify>();
publishingServer.stop();
}
{
RCF::RcfServer subscribingServer(endpoint);
RCF::SubscriptionServicePtr subscriptionServicePtr(
new RCF::SubscriptionService() );
subscribingServer.addService( subscriptionServicePtr );
subscribingServer.start();
Notify notify;
subscriptionServicePtr->beginSubscribe<I_Notify>(
notify,
RCF::TcpEndpoint(ip, port));
// notify.func1() and notify.func2() will
// now be remotely called by the publisher.
// ...
subscriptionServicePtr->endSubscribe<I_Notify>(notify);
}
可扩展性
RcfServer服务
该RcfServer
课程通过服务理念适应第三方扩展。当服务器启动和停止时,服务将被通知。此外,服务器可以在服务器运行时从服务器中动态添加和删除。典型的服务可以将对象绑定到服务器上的接口,并且还可以请求服务器产生一个线程(或产生自己的线程)来进行某种定期活动。
服务器传输实现为服务。因此,可以具有服务于单个RcfServer
对象的多个传送。有些服务可用:
ObjectFactoryService
:允许客户端在服务器上创建对象FilterService
:允许服务器根据客户端请求动态加载过滤器PublishingService
:启用在服务器上的发布功能SubscriptionService
:启用服务器上的订阅功能
可移植性
编译器
RCF已经针对大量编译器进行了测试,其中包括Microsoft Visual C ++(6.0版本),Borland C ++(5.6版)和GCC(2.95版)。有关完整列表,请参阅“ RCF用户指南”。
平台
RCF的TCP服务器实现现在基于Win32 I / O完成端口,这种技术限于Windows 2000及更高版本。TCP客户端实现和UDP服务器/客户端实现基于BSD套接字,应该是广泛的便携式。在非Windows平台上,也可选择在Windows上,RCF利用Boost.Asio库来实现TCP服务器。
建造
一般来说,要使用RCF构建应用程序,您需要在应用程序的源代码中包含src / RCF / RCF.cpp文件。您还需要Boost库的头文件; 自1.33.0以来的任何版本都可以。
有几个预处理器符号可用于控制RCF的哪些部分被编译。这些符号需要在全局范围内定义,即它们应该放在项目设置中,而不是在源代码中定义/未定义。
RCF_USE_BOOST_THREADS
:利用Boost.Thread库进行互斥和线程生成功能。如果没有定义,RCF将使用自己的内部线程库。RCF_USE_BOOST_ASIO
:利用Boost.Asio网络库。在非Windows平台上需要服务器端代码。RCF_USE_ZLIB
:编译支持Zlib压缩。RCF_USE_OPENSSL
:编译支持OpenSSL加密。RCF_USE_BOOST_SERIALIZATION
:编译支持Boost.Serialization库。RCF_USE_SF_SERIALIZATION
:编译支持RCF的内置序列化框架。如果既不定义RCF_USE_BOOST_SERIALIZATION
也不RCF_USE_SF_SERIALIZATION
定义自动定义。RCF_NO_AUTO_INIT_DEINIT
:禁用RCF的自动初始化/去初始化设施。如果定义,用户将必须明确地调用RCF::init()
并RCF::deinit()
在适当的时间。特别地,当将RCF编译为DLL以避免过早初始化时,这是必要的。
所有第三方构建依赖项(Boost.Threads,Boost.Serialization,Zlib,OpenSSL)都是可选的。构建这些库的说明超出了本文的范围,但是如果您在构建Boost库时遇到问题,则可以选择绕过Boost的构建实用程序bjam,而只需将相应的CPP文件编译到应用程序中即可。
例如,要使用Boost.Threads库,您可以在项目中的boost_root / libs / thread / src中包含CPP文件。然后,定义boost_root / boost / thread / detail / config.hpp中的一个符号(可能是适当的BOOST_THREAD_USE_LIB
)。
测试
下载的/ test目录中有一套全面的测试,所有这些测试都应该编译并运行而不会出现故障。可以使用Boost.Build实用程序自动构建和运行测试,或者手动构建和运行。测试运行的功能比我在这里提到的功能更多,可能作为用户信息来源很有用。
历史
- 2005-12-23 - 版本0.1发布
- 2006-04-06 - 版本0.2发布
RCF现在在Linux和Solaris以及Windows上编译和运行。服务器和客户端可以分布在多个平台上,并且仍然无缝通信。
该短耳网络库是在非Windows平台上使用RCF的先决条件。下载Asio,确保编译器可以使用Asio头,并确保
RCF_USE_ASIO
在编译RCF时定义了预处理器符号。Asio需要Boost的1.33.0或更新版本,您可能希望定义BOOST_DATE_TIME_NO_LIB
何时使用它,以避免不必要的Boost依赖关系。RCF 0.2已经在以下编译器上进行了编译和测试:
- GCC 3.2(Windows上的MinGW)
- GCC 3.3(Linux)
- GCC 3.4(Solaris)
- Borland C ++ 5.6
- Metrowerks CodeWarrior 9.2
- Microsoft Visual C ++ 7.1
- Microsoft Visual C ++ 8.0
在这个论坛上报道的各种错误也已经修复。
对任何正在等待这个发布的人道歉...我原本计划在几个月前发布,但忙碌的工作时间表(和一些固执的测试用例)干预了。抱歉!
- 2006-07-30 - 版本0.3发布
- 现在,RCF对Asio的支持已经由David Bergman从0.3.5升级到0.3.7版本。谢谢大卫!
- 对Asio 0.3.5的支持已经下降。
- 预处理器符号
RCF_USE_ASIO
现在命名RCF_USE_BOOST_ASIO
。 - 这个论坛上报道的杂项问题也已经解决。
- 2006-09-19 - 版本0.4发布
- 64位兼容性:RCF现在可在64位Solaris,Linux和Windows平台上编译和运行。
- 的快速序列化
std::vector<T>
,其中T
是原语(char
,int
等)。 - 对于那些使用RCF在32位和64位系统之间通信的用户,可以避免使用类似于RCF接口
long
或类似std::size_t
RCF接口的类型,从而避免由32位和64位系统之间的大小差异引起的序列化错误。使用的升压一个typedef
!而非(boost::int32_t
,boost::uint32_t
,boost::int64_t
,boost::uint64_t
)。 - 最后,感谢不伦瑞克技术大学的Freudiger,为64位的Linux机器借了我一个帐号!
- 2007-07-11 - 版本0.9c发布
- RCF 0.9c,RCF 1.0的预发行版本现在可从Google Code的下载部分获得。在发展一年多的时间里,RCF 0.9c是RCF 0.4的重大改组和升级。已经将其作为一个主要的商业ECM平台的网络后端,已经被彻底的放纵了。
- 使用RCF 0.4的应用程序在升级到RCF 0.9c方面的难度相对较小。如果您将应用程序移植到0.9c有任何问题,请随时通过本论坛或通过电子邮件与我联系。我会很乐意帮助你。
- RCF 0.9c功能包括:
- 零拷贝,零堆分配核心,实现快速和可扩展的性能。
- SSPI过滤器,用于Windows平台上的透明Kerberos和NTLM身份验证和加密。
- OpenSSL过滤器,用于透明的SSL认证和加密。
- 服务器端多线程
- 服务器端会话对象。
- 内置运行时版本控制,用于向后和向前运行时兼容。
- 强大的发布/订阅功能。
- 支持遗留编译器,特别是Visual C ++ 6,Borland C ++ Builder 6和GCC 2.95。
- 支持64位编译器。
- 2007-08-23 - 文章内容更新
- 2008-04-28 - 版本0.9d-P1发布
- RCF 0.9d-P1是RCF-0.9d的预览。它已经在Windows上完全测试,与Visual C ++编译器的范围(6.0,7.1,8.0,9.0)完全相同。RCF 0.9d版本将包括对Linux和Solaris的全面支持,以及用户指南形式的大量文档。
- 从Google代码网站的下载页面获取。
- RCF 0.9d功能包括:
- Win32命名管道传输实现(
RCF::Win32NamedPipeEndpoint
)。 - Boost.Thread不再需要。
- UDP组播和广播。
- SF序列化
boost::tuple
,boost::variant
和boost::any
。 - 支持从DLL导出RCF。
- 兼容最新的Boost(1.35.0)和Boost.Asio(0.3.8+)版本。
- 与RCF 0.9c保证电线兼容。
- Win32命名管道传输实现(
- 2008-07-20 - 文章内容更新
- 2008-07-20 - 版本0.9d发布
- 2008-10-22 - 版本1.0发布
- 2009-07-10 - 版本1.1发布
- RCF 1.1现在可以下载。
- RCF 1.1功能包括:
- Ping功能添加到
RCF::ClientStub
。 - 服务器到客户端pingback,用于在长时间运行的呼叫(
RCF::PingBackService
)中维护连接。 - 服务器到客户端的回调。
- 动态线程池增长和收缩,以适应客户端负载。用户级代码不再需要调用
ThreadManager::notifyBusy()
。 - 所有运输的进度回调。
- 基于Schannel的传输过滤器,用于Windows平台上的SSL加密。
- 针对Boost版本测试,最高1.39.0。
- 支持
__attribute__(visibility())
使用GCC 4.x构建共享库时。 - 内存使用优化。
- Ping功能添加到
- Visual C ++ 6用户需要
auto_ptr<>::release()
在Visual C ++ 6 STL实现中应用修复。 - 通常情况下,RCF用户指南中的详细信息。
- 2010-02-15 - 版本1.2发布
- RCF 1.2现在可供下载。
- RCF 1.2功能包括:
- 支持Google的协议缓冲区(文档在这里)。
- 支持批量的单向呼叫(此处的文档)。
- 针对Boost版本测试,高达1.42.0。
- 如果您从早期版本的RCF升级到RCF 1.2,则需要注意以下几个突破性更改:
- SF序列化功能现在具有不同的签名。冗余版本参数已被删除,因此对于外部序列化功能,签名现在为“
void serialize(SF::Archive &, SomeType & t)
”。对于内部序列化功能,签名现在为“void SomeType::serialize(SF::Archive &)
”。 - 全局
serializeParent()
函数已被移入SF
命名空间。 - 该
RCF::Exception::getError()
功能已重命名为getErrorId()
。
- SF序列化功能现在具有不同的签名。冗余版本参数已被删除,因此对于外部序列化功能,签名现在为“
- “ RCF用户指南”中提供了发行说明。
- 2011-01-06 - 版本1.3发布
- 2011-10-25 - 版本1.3.1发布
- RCF 1.3.1现在可供下载。
- RCF 1.3.1是RCF 1.3的错误修复版本。修正错误:
- 在RCF方法签名中使用signed char时修复编译器错误。
- 使用时修复性能问题
RCF::SspiFilter
。现在,将多个小消息块合并成一个较大的块,以提高网络性能。 - 改进的SF序列化性能。只有
typeid()
在必要时才打电话 - 减少SF档案大小。使用单个字节而不是4个字节对小整数进行编码。
- 在使用基于Boost.Asio的传输的多线程线程池时,修复了CPU使用率过高。
- 修正为
boost::any
序列化。空的boost::any
实例导致抛出异常。 - 修正使用Windows
MsgWaitForMultipleObjects()
功能轮询网络连接时客户端超时逻辑的错误。 - 在
RcfServer
运行时不能再添加或删除服务。 - 在封送逻辑中固定潜在的空指针崩溃。
- 重命名变量命名
signals
和slots
为了不干扰QT预处理器。 - 修复了在OSX上导致编译器警告的预处理器重新定义。
- “ RCF用户指南”中提供了发行说明。
- Web论坛现在可用于与RCF相关的问题和意见。
许可证
请注意:每个下载的代码是完全独立的。文章和代码的许可证如下:
- 该文章根据“代码项目开放许可证(CPOL)”获得许可。
- RCF 0.4根据麻省理工学院的许可证获得许可。
- RCF 0.9c及更高版本根据GPL许可证获得许可。