CORBA/TAO使用手记1 -- C/S架构网络通信开发
转载: http://www.cppblog.com/cppx/archive/2011/02/16/140184.html
在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)实在是原始而又弱智的梦呓。
先写到这里吧,关于客户端调用的方法,下回分解!