在C/S结构的C++网络程序中,直接采用Socket API进行开发效率是很低的,所以大家发明了各种各样的网络框架,如Boost.Aiso和ACE,简化了网络通信开发的难度。
但是这种基于数据包收发的模式还是不太方便,于是又出现了RPC、DCOM、CORBA等远程接口调用的标准。客户端只需要像调用本地函数一样调用远程接口,框架会自动处理数据包收发,请求和应答等底层细节。
虽然现在Web技术的发展如火如荼,大有取代C/S架构应用之势,但是,直接运行于操作系统平台上的C++原生应用还是有它存在的意义,最主要的方面就是接近系统底层,对操作系统资源和底层设备的控制等,其他任何虚拟机上的中间语言是无法望其项背的。
CORBA是一个为简化跨平台应用而提出的规范,它独立于网络协议、编程语言和软硬件平台,支持异构的分布式计算环境和不同编程语言间的对象重用。
CORBA可以作为不同平台应用间信息传递的中间件,CORBA通过引入经过充分验证的有效的框架结构和通信手段,最大限度地简化了网络通信相关应用的设
计与开发,使得我们可以专注于业务逻辑的实现,而无需关心通信的细节。CORBA曾在无数文章中被称作“软总线”,以表明它作为数据传递通道的基本特性。
现在存在众多CORBA实现,既有商用的ORBacus、VisiBroker,也有一些优秀的开源实现,如:TAO、omniORB、MICO等。由
于各实现遵从相同的规范,接口基本一致,所以在熟练应用一种CORBA实现后,转而使用其它实现时,一般不会存在太大的障碍。
TAO(The ACE ORB)是美国华盛顿大学的Douglas C. Schmidt教授领导开发的一个实时CORBA平台,它是一个免费的开放源码项目,用C++语言开发,符合CORBA2.6规范。
支持语言: C++
支持平台: Win32,常见的各种Unix/Linux,实时操作系统如VxWorks等等。在所有的CORBA实现中,TAO支持的平台是最多的。
支持的服务:
Naming、Event、Notification、Security、Time、Scheduling、Logging、Lifecycle、
Trading、Concurrency、Lifecycle、A/V Streaming、Load balancing等。
在日
常工作中我们经常使用的服务就是Naming服务和Notification服务了。Naming服务让你可以把自己的远程对象注册到一个索引服务中,使
用者通过友好的注册名即可找到提供服务的对象,而不用关心这个对象运行在那台机器上。Notification服务给客户端提供了主动推送消息的机制,而
不需要客户端不断查询是否有自己感兴趣的消息。
那么在CORBA中客户端和服务端通过什么方式约定双方的通讯协议呢?这就是IDL语言的功劳
了,IDL以一种通用的方式为C/S双方提供了接口的描述,语法非常类似于C++或Java的类的描述。CORBA提供了专门的工具用来把IDL语言编写
的接口文件编译成C++或Java能够理解的桩和框架类,服务端基于IDL编译器生成的“框架”添加服务实现,客户端调用IDL编译器生成的“桩”调用远
程服务的接口,双方都不需要关心对方内部如何实现,通信数据包格式等问题,IDL会为你搞定一切。
百闻不如一见,通过一个真实的IDL文件例子,看一下接口的定义到底是如何工作的:
1 #ifndef ScriptManageService_h__
2 #define ScriptManageService_h__
3
4 #include "ALEE_ScriptManageDefines.idl"
5
6 module alee {
7 module ScriptManage {
8
9 //////////////////////////////////////////////////////////////////////////
10 // 脚本管理服务
11 //////////////////////////////////////////////////////////////////////////
12 interface ALEE_ScriptManageService {
13
14 // 获取事件类型列表
15 boolean apiGetEventTypes( out EventTypeList_t list );
16
17 // 列出所有脚本
18 boolean apiGetScriptList( out ScriptList_t list);
19
20 // 获取脚本信息
21 boolean apiGetScriptInfo( in long ID, out ScriptInfo_t info );
22
23 // 保存脚本,ID>0时修改,ID=0时新增,返回新增的ID或原有ID
24 boolean apiSetScriptInfo( in long ID, in ScriptInfo_t info );
25
26 // 删除脚本
27 boolean apiDeleteScript( in long ID );
28 };
29
30 };
31 };
32
33 #endif // ScriptManageService_h__
34
从这个IDL文件中可以看出,这个名为“脚本管理服务”的接口interface ALEE_ScriptManageService中定义了5个方法,调用IDL编译器编译后,生成了下面的6个文件,其中前3个是客户端的“桩”,后3个是服务端的“框架”。
然后,就需要我们来实现服务端的接口了,在ALEE_ScriptManageServiceS.h文件中IDL编译器生成了一个C++的类class ALEE_ScriptManageService,其中包含的几个纯虚函数就是我们定义的IDL接口方法。
1 namespace POA_alee
2 {
3 namespace ScriptManage
4 {
5 class UBISALEE_API ALEE_ScriptManageService
6 : public virtual PortableServer::ServantBase
7 {
8 protected:
9 ALEE_ScriptManageService (void);
10
11 public:
12 ALEE_ScriptManageService (const ALEE_ScriptManageService& rhs);
13 virtual ~ALEE_ScriptManageService (void);
14
15 virtual ::CORBA::Boolean apiGetEventTypes (::alee::ScriptManage::EventTypeList_t_out list) = 0;
16 virtual ::CORBA::Boolean apiGetScriptList (::alee::ScriptManage::ScriptList_t_out list) = 0;
17 virtual ::CORBA::Boolean apiGetScriptInfo (::CORBA::Long ID,::alee::ScriptManage::ScriptInfo_t_out info) = 0;
18 virtual ::CORBA::Boolean apiSetScriptInfo (::CORBA::Long ID,const ::alee::ScriptManage::ScriptInfo_t & info) = 0;
19 virtual ::CORBA::Boolean apiDeleteScript (::CORBA::Long ID) = 0;
20 };
21 } // module alee::ScriptManage
22 } // module alee
23
我们要实现这个接口,就需要添加一个子类继承这个接口类:
1
2 class ALEE_ScriptManageService_i : public POA_alee::ScriptManage::ALEE_ScriptManageService
3 {
4 ORB_Objects & m_orb;
5 ALEE_DataBase_Impl &m_dat;
6 ALEE_ScriptList_t & m_scripts;
7
8 public:
9 ALEE_ScriptManageService_i(ORB_Objects &orb,ALEE_DataBase_Impl &dat,ALEE_ScriptList_t & scripts);
10 ~ALEE_ScriptManageService_i(void);
11
12 virtual CORBA::Boolean apiGetEventTypes(EventTypeList_t_out list);
13 virtual CORBA::Boolean apiGetScriptList(ScriptList_t_out list);
14 virtual CORBA::Boolean apiGetScriptInfo(CORBA::Long ID,ScriptInfo_t_out info);
15 virtual CORBA::Boolean apiSetScriptInfo(CORBA::Long ID,const ScriptInfo_t & info);
16 virtual CORBA::Boolean apiDeleteScript (CORBA::Long ID);
17 };
18
具体实现方法不必多言,就像写一个普通的C++类一样即可。我们关心的问题是服务类写好了,客户端如何知道这个类在哪里?
下面就需要把我们的接口实现注册到Naming服务中,ORB_Objects类是一个ORB对象的封装类,是为了方便而把ORB下文相关的对象都放在了一起,暂时把它当成是个ORB的句柄,负责与软总线ORB通讯即可。
1
2 #define SERVICE_NAME "ALEE_ScriptManageService"
3
4 ALEE_ScriptManageService_i::ALEE_ScriptManageService_i(ORB_Objects & orb,
5 ALEE_DataBase_Impl & dat,
6 ALEE_ScriptList_t & scripts) :
7 m_orb(orb),
8 m_dat(dat),
9 m_scripts(scripts)
10 {
11 m_orb.rebind_name(SERVICE_NAME,_this());
12 }
13
m_orb是在服务端启动时初始化的一个ORB_Objects实例,在创建“脚本管理服务”时通过构造函数传入了一个引用,在服务类实例化时,调用m_orb把自己注册到Naming服务里,这样客户端就可以通过访问名字服务知道我们的服务所在的位置。
上手了CORBA之后,才发现自己原来设想的
C++实现远程服务对象调用(
http://www.cppblog.com/cppx/archive/2009/07/22/90820.html)实在是原始而又弱智的梦呓。
先写到这里吧,关于客户端调用的方法,下回分解!