CORBA 上的简单 C++ 客户端/服务器






4.71/5 (28投票s)
Visual C++ CORBA开发入门。
目录
1 引言
对象管理组织 (Object Management Group) 是一个推广面向对象技术使用的非营利性组织。除其他事项外,它定义了 UML 和 CORBA 标准。
CORBA 是 通用 ORB 体系结构 (Common ORB Architecture) 的首字母缩写。通用体系结构 的说法是指一种技术标准,因此 CORBA 仅仅是一种称为 ORB 的技术标准。
反过来,ORB 是 对象请求代理 (object request broker) 的首字母缩写,它是旧技术 远程过程调用 (RPC) 的面向对象版本。ORB 或 RPC 是一种调用不同(“远程”)进程中的对象(或调用过程)上的操作的机制,这些进程可能运行在同一台计算机或不同的计算机上。在编程层面,这些“远程”调用看起来与“本地”调用相似。实际上,CORBA 使使用一种编程语言(例如 Java)编写的客户端应用程序能够远程调用用另一种编程语言(例如 C++)实现的服务器。这种语言独立性源于服务器应用程序的公共接口定义在 IDL 文件中,并且 CORBA 定义了从 IDL 到多种编程语言(包括 C、C++、Java、COBOL、Ada、SmallTalk 和 Python)的映射。
1.1 接口定义语言 (IDL)
IDL 文件定义了服务器应用程序中对象暴露的公共应用程序编程接口 (API)。CORBA 对象的类型称为 interface
,其概念类似于 C++ 的 class
或 Java 的 interface
。下面显示了一个 IDL 文件的示例。
module Finance {
typedef sequence<string> StringSeq;
struct AccountDetails {
string name;
StringSeq address;
long account_number;
double current_balance;
};
exception insufficientFunds { };
interface Account {
void deposit(in double amount);
void withdraw(in double amount) raises(insufficientFunds);
readonly attribute AccountDetails details;
};
};
如上例所示,IDL 类型可以分组到 module
中。此构造的作用类似于 C++ 的 namespace
或 Java 的 package
:它为类型名称添加前缀以防止命名空间污染。IDL 中的作用域运算符是“::
”。例如,Finance::Account
是在 Finance
模块中定义的 Account
类型的完全限定作用域名称。
IDL interface
可以包含操作和属性(如果您愿意,还可以包含嵌套类型)。许多人错误地认为 attribute
的概念类似于 C++ 中的实例变量(Java 中的 field)。这是错误的。attribute
只是用于一对 get- 和 set 式操作的语法糖。attribute
可以是 readonly
,在这种情况下它只映射到一个 get 式操作。
操作的参数具有指定的方向,可以是 in
(表示参数从客户端传递到服务器)、out
(参数从服务器传递回客户端)或 inout
(参数双向传递)。操作还可以具有返回值。如果发生错误,操作可以引发(抛出)一个异常。有超过 30 种预定义的异常类型,称为系统异常,所有操作都可以抛出这些异常,尽管实际上系统异常比应用程序代码更频繁地由 CORBA 运行时系统引发。除了预定义的系统异常外,还可以在 IDL 文件中定义新的异常类型。这些称为用户定义的异常。操作签名的 raises
子句指定了它可能抛出的用户定义异常。
操作的参数(和返回值)可以是内置类型之一——例如,string
、boolean
或 long
——或者是 IDL 文件中声明的“用户定义”类型。用户定义类型可以是以下任何一种:
struct
。这类似于 C/C++ 的struct
或只包含public
字段的 Javaclass
。sequence
。这是一种集合类型。它类似于一个可以增长或缩小的⼀维数组。IDL 到 C++ 的映射早于 C++ 标准模板库,因此不幸的是,IDLsequence
不映射到std::vector
。相反,IDL 到 C++ 的映射定义了自己的类似向量的数据类型。- 数组。IDL 数组的维度在 IDL 文件中指定,因此数组的大小是固定的,即它在运行时不能增长或缩小。数组在 IDL 中很少使用。
sequence
类型更灵活,因此更常用。 typedef
。这为现有类型定义了一个新名称。例如,下面的语句定义了以short
表示的age
typedef short age;
typedef
的一个常见且非常重要的用途是将名称与序列或数组声明关联起来。例如typedef sequence<string> StringSeq;
union
。此类型可以在运行时保存多个值中的一个,例如union Foo switch(short) { case 1: boolean boolVal; case 2: long longVal; case 3: string stringVal; };
Foo
的实例可以保存一个boolean
、一个long
或一个string
。case
标签指示当前哪个值是活动的。enum
在概念上类似于一组常量整数声明。例如enum color {red, green, blue};
在内部,CORBA 使用整数值来表示不同的
enum
值。
1.2 IDL 到编程语言的映射
如前所述,IDL 用于定义服务器应用程序中对象暴露的 public
API。IDL 以独立于任何特定编程语言的方式定义此 API。然而,为了使 CORBA 有用,必须存在从 IDL 到特定编程语言的映射。例如,IDL 到 C++ 的映射使人们能够用 C++ 开发 CORBA 应用程序,而 IDL 到 Java 的映射使人们能够用 Java 开发 CORBA 应用程序。
CORBA 标准目前定义了从 IDL 到以下编程语言的映射:C、C++、Java、Ada、Smalltalk、COBOL、PL/I、LISP、Python 和 IDLScript。这些官方认可的语言映射提供了应用程序在不同 CORBA 产品之间的源代码可移植性。对于 Eiffel、Tcl 和 Perl 等一些其他语言,存在非官方映射。
1.3 IDL 编译器
IDL 编译器将 IDL 定义(例如 struct
、union
、sequence
等)转换为编程语言(如 C++、Java、Ada 或 Cobol)中的类似定义。此外,对于每个 IDL interface
,IDL 编译器都会生成存根代码——也称为代理类型——和骨架代码。这些术语经常引起混淆,因此我在下面进行解释
- stub 的字典定义是“用完更大的东西后剩下的短部分,例如,铅笔的笔杆或香烟的烟蒂”。在传统的(非分布式)编程中,stub procedure 是过程的虚拟实现,用于防止链接时出现“未定义标签”错误。在像 CORBA 这样的分布式中间件系统中,远程调用是通过客户端调用一个 stub 过程/对象来实现的。存根使用进程间通信机制(如 TCP/IP 套接字)将请求传输到服务器进程并接收响应。
- proxy 这个词经常用来代替 stub。proxy 的字典定义是“被授权代表他人行事的人”。CORBA 代理只是一个客户端对象,它代表服务器进程中的“真实”对象。当客户端应用程序在代理上调用操作时,代理会使用进程间通信机制将请求传输到服务器进程中的“真实”对象;然后代理等待接收响应,并将此响应传递回客户端的应用程序代码。
- skeleton code 这个词指的是服务器端用于读取传入请求并将其分派到应用程序级别对象的代码。skeleton 这个词似乎是个奇怪的选择。然而,skeleton 这个词的使用并不仅限于骨骼的讨论;更广泛地说,它表示“支持性基础设施”。Skeleton code 之所以这样命名,是因为它提供了实现服务器应用程序所需的支持性基础设施。
CORBA 产品必须提供 IDL 编译器,但 CORBA 规范并未说明编译器的名称或它接受的命令行选项。这些细节因 CORBA 产品而异。
1.4 进行远程调用
CORBA 使用 servant 这个词来指代用编程语言(例如 C++ 或 Java)编写并实现 IDL 接口的对象。下图显示了客户端应用程序调用服务器进程中的对象/服务程序时发生的情况。

客户端中的应用程序代码调用本地代理对象(回想一下,代理类是由 IDL 编译器生成的)。代理将关于请求的信息——例如正在调用的操作的名称及其in和inout参数——封组成一个二进制缓冲区。然后,代理对象将此二进制缓冲区传递给 ORB 库(由 CORBA 产品提供),后者通过网络将请求消息传输到服务器进程。客户端进程中的 ORB 等待从服务器进程读取响应消息。ORB 将响应缓冲区返回给代理对象,代理对象解组inout和out参数和返回值(或引发的异常),并将它们返回给客户端应用程序代码。
在服务器端,ORB 中的一个线程位于事件循环中,等待传入的请求。当请求到达时,ORB 读取请求的二进制缓冲区,并将其传递给一些代码,这些代码会解组参数并将请求分派到目标服务程序。执行解组和分派的代码分布在两个组件中。一个组件称为 POA,我将在本文后面讨论。另一个组件是由 IDL 编译器生成的骨架代码。生成的骨架代码没有在图中明确显示,因为它采用基类的形式,服务程序类会继承该基类。当服务程序中的操作返回时,骨架代码会将inout和out参数(或引发的异常)封组成一个二进制缓冲区,并通过 POA 返回给服务器端 ORB,服务器端 ORB 将响应消息通过网络传输到客户端进程。
1.5 CORBA 服务
许多编程语言都配有标准化的函数和/或类库,以补充核心语言。这些标准化库通常提供集合数据类型(例如,链表、集合、哈希表等)、文件输入输出以及对开发各种应用程序有用的其他功能。如果您要求开发人员用例如 Java、C 或 C++ 编写应用程序,但不使用该语言的标准库,那么开发人员会发现这非常困难。
CORBA 的情况类似。CORBA 的核心部分(一个用 IDL 和通用线协议构建的面向对象 RPC 机制)本身用途有限——就像一个被剥离了标准化库的编程语言用途有限一样。极大地增强 CORBA 功能的是一套标准化的服务——称为 CORBA Services——它们提供了开发各种分布式应用程序的有用功能。CORBA Services 具有用 IDL 定义的 API。实际上,您可以将 CORBA Services 视为一个标准化的类库。但是,需要注意的一点是,大多数 CORBA Services 是以预构建的服务器应用程序的形式提供的,而不是作为链接到您自己的应用程序中的库。因此,CORBA Services 实际上是一个分布式的标准化类库。
一些常用的 CORBA Services 包括以下内容
- 命名服务和交易服务允许服务器应用程序发布其对象,从而使客户端应用程序易于找到对象。
- 大多数 CORBA 应用程序使用同步、一对一通信。然而,一些应用程序需要多对多、异步通信,或者许多人称之为发布-订阅通信。已定义了各种 CORBA Services 来支持此类通信。
- 在分布式系统中,有时需要一个事务跨越多个数据库,以便在事务提交时,保证所有数据库都已更新或均未更新。对象事务服务 (OTS) 提供了此功能。
2 开发简单的客户端-服务器应用程序
我使用了 Orbacus 对 COBRA 的实现来开发一个示例客户端-服务器应用程序。Orbacus 可用于 Java 和 C++。
2.1 业务逻辑领域
我的演示服务器应用程序提供 encrypt()
和 decrypt()
操作。客户端应用程序调用服务器中的对象上的 encrypt()
来加密一些明文。稍后,客户端调用 decrypt()
来再次解密文本。
加密/解密算法基于凯撒密码,这是最简单的加密技术之一。它是一种替换密码,其中明文中的每个字母都替换为字母表中按固定数量位置偏移的字母。我在移位操作后添加了一个与加密密钥的 XOR 操作,以模糊它们之间的关系。您可以在下面的图表中看到替换技术

2.2 编写 IDL
实现客户端-服务器应用程序的第一步是编写一个 IDL 文件,该文件定义服务器的公共 API。这包括一个提供 encrypt()
和 decrypt()
操作的接口。该接口还包含一个 shutdown()
操作,以便我们可以优雅地请求服务器终止。
下面显示了演示应用程序的 IDL
// Crypt.idl
interface CaesarAlgorithm {
typedef sequence<char> charsequence;
charsequence encrypt(in string info,in unsigned long k,in unsigned long shift);
string decrypt(in charsequence info,in unsigned long k,in unsigned long shift);
boolean shutdown();
};
2.3 生成骨架和存根
编写 IDL 文件后,我们通过运行 Orbacus 提供的 IDL 编译器将 IDL 定义转换为相应的 C++ 定义。
idl crypt.idl
运行该命令会生成以下文件
- crypt.h 和 crypt.cpp:这些文件定义并实现了对应于 IDL 文件中定义的 IDL 类型(如 struct、union、sequence 等)的 C++ 类型。这些文件还实现了对应于 IDL 接口的客户端代理类。
- crypt_skel.h 和 crypt_skel.cpp:这些文件定义并实现了服务器端所需的功能,用于读取传入请求并将它们分派到服务程序(代表 CORBA 对象的 C++ 对象)。
请注意,CORBA 规范尚未标准化 IDL 编译器的名称,不同的 CORBA 实现可能使用不同的名称。例如,Orbacus、Orbix 和 omniORB 中的 IDL 编译器称为 idl
,而 TAO 将其 IDL 编译器称为 tao_idl
,VisiBroker 和 Orbit 都称其为 idl2cpp
。
2.4 实现服务程序类
下一步是实现服务程序类,即实现 IDL 接口的 C++ 类。为此,我们创建一个类(CryptographicImpl
),该类继承自 IDL 编译器生成的骨架类(POA_CaesarAlgorithm
)。您可以在下面看到这一点。
#include <iostream>
#include <string>
#include "OB/CORBA.h"
#include "crypt_skel.h"
class CryptographicImpl : virtual public ::POA_CaesarAlgorithm,
virtual public PortableServer::RefCountServantBase
{
CORBA::ORB_var orb; // Reference to CORBA ORB
public:
CryptographicImpl(CORBA::ORB_var orb)
{
this->orb = orb;
}
// Caesar text encryption algorithm
virtual ::CaesarAlgorithm::charsequence*
encrypt(const char* info,::CORBA::ULong k,::CORBA::ULong shift)
throw(::CORBA::SystemException)
{
std::string msg = info;
int len = msg.length();
::CaesarAlgorithm::charsequence* outseq =
new ::CaesarAlgorithm::charsequence;
outseq->length(len + 1);
std::string::iterator i = msg.begin();
std::string::iterator end = msg.end();
int j = 0;
while (i != end)
{
*i+= shift;
*i ^= k;
(*outseq)[j++] = *i++;
}
(*outseq)[len] = '\0';
return outseq;
}
// Caesar text decryption algorithm
virtual char* decrypt(const ::CaesarAlgorithm::charsequence&
info,::CORBA::ULong k,::CORBA::ULong shift)
throw(::CORBA::SystemException)
{
char* r = CORBA::string_alloc(info.length());
for (int i = 0;i < info.length() - 1;i++)
{
r[i] = info[i];
r[i] ^= k;
r[i] -= shift;
}
r[info.length() - 1] = '\0';
return r;
}
// Terminate CORBA message
virtual ::CORBA::Boolean shutdown() throw(::CORBA::SystemException)
{
orb->shutdown(false);
return true;
}
};
服务程序实现了 IDL 接口中定义的操作。当客户端发出远程调用时,分派代码(在继承的 POA_CaesarAlgorithm
类中实现)会解组传入请求的参数并调用操作。
2.5 创建服务器
在实现了服务程序类之后,我们现在必须实现服务器的主程序。这会执行一些 CORBA 初始化步骤,创建服务程序,在命名服务中发布其对象引用,然后进入事件循环以等待传入请求。在显示代码之前,我需要解释另外两个 CORBA 概念:POA 和 POA Manager。
到目前为止,通过阅读本文,您可能会认为客户端调用远程对象上的操作,而这些远程对象存在于服务器进程中。这几乎是正确的。实际上,对象存在于一个名为 POA 的容器中(该术语代表可移植对象适配器,但名称的起源并不特别有趣),而 POA 又存在于服务器进程中。这样做的动机是,不同的 POA 可以提供不同的服务质量(或 CORBA 术语中的策略)。例如,一个 POA 可能是单线程的,而另一个 POA 可能是多线程的。POA 提供的服务质量应用于该 POA 中的服务程序。因此,通过将一些服务程序放入一个 POA 中,而将其他服务程序放入另一个 POA 中,单个服务器进程可以托管具有各种不同服务质量的对象:一些对象可能是单线程的,而另一些是多线程的;一些对象可能是瞬态的(表示临时的),而其他对象可能是持久的(表示客户端应用程序即使在服务器进程崩溃并重启后仍能继续与该对象通信)。CORBA 提供了一个 API,使服务器开发人员能够创建多个 POA,每个 POA 具有潜在不同的服务质量。服务器中的 CORBA 运行时系统预先创建了一个“rootPOA”,该 POA 是多线程且瞬态的。由于这种服务质量适合我们演示服务器的需求,因此我们无需显式创建 POA。
水龙头(或美国人称之为faucet)用于打开和关闭水流。POA Manager 类似于水龙头,但它控制传入请求的流。当 CORBA 服务器启动时,与 root POA 关联的 POA 管理器处于挂起状态,这意味着如果任何来自客户端的请求到达,它们将被排队。这种挂起状态使服务器进程能够完成其初始化,而无需担心处理传入的请求。当服务器完成初始化后,它将其 POA 管理器置于活动状态,以便可以将传入的请求通过 POA 分派给服务程序。
#include <iostream>
#include "OB/CORBA.h"
#include <OB/Cosnaming.h>
#include "crypt.h"
#include "cryptimpl.h"
using namespace std;
int main(int argc, char** argv)
{
// Declare ORB and servant object
CORBA::ORB_var orb;
CryptographicImpl* CrypImpl = NULL;
try {
// Initialize the ORB.
1 orb = CORBA::ORB_init(argc, argv);
// Get a reference to the root POA
2 CORBA::Object_var rootPOAObj =
orb->resolve_initial_references("RootPOA");
// Narrow it to the correct type
PortableServer::POA_var rootPOA =
PortableServer::POA::_narrow(rootPOAObj.in());
// Create POA policies
CORBA::PolicyList policies;
policies.length(1);
policies[0] =
rootPOA->create_thread_policy
(PortableServer::SINGLE_THREAD_MODEL);
// Get the POA manager object
PortableServer::POAManager_var manager = rootPOA->the_POAManager();
3 // Create a new POA with specified policies
PortableServer::POA_var myPOA = rootPOA->create_POA
("myPOA", manager, policies);
// Free policies
CORBA::ULong len = policies.length();
for (CORBA::ULong i = 0; i < len; i++)
policies[i]->destroy();
// Get a reference to the Naming Service root_context
4 CORBA::Object_var rootContextObj =
orb->resolve_initial_references("NameService");
// Narrow to the correct type
CosNaming::NamingContext_var nc =
CosNaming::NamingContext::_narrow(rootContextObj.in());
// Create a reference to the servant
5 CrypImpl = new CryptographicImpl(orb);
// Activate object
PortableServer::ObjectId_var myObjID =
myPOA->activate_object(CrypImpl);
// Get a CORBA reference with the POA through the servant
6 CORBA::Object_var o = myPOA->servant_to_reference(CrypImpl);
// The reference is converted to a character string
CORBA::String_var s = orb->object_to_string(o);
7 cout << "The IOR of the object is: " << s.in() << endl;
CosNaming::Name name;
name.length(1);
name[0].id = (const char *) "CryptographicService";
name[0].kind = (const char *) "";
// Bind the object into the name service
8 nc->rebind(name,o);
// Activate the POA
manager->activate();
cout << "The server is ready.
Awaiting for incoming requests..." << endl;
// Start the ORB
9 orb->run();
} catch(const CORBA::Exception& e) {
// Handles CORBA exceptions
cerr << e << endl;
}
// Decrement reference count
if (CrypImpl)
10 CrypImpl->_remove_ref();
// End CORBA
if (!CORBA::is_nil(orb)){
try{
11 orb->destroy();
cout << "Ending CORBA..." << endl;
} catch (const CORBA::Exception& e)
{
cout << "orb->destroy() failed:" << e << endl;
return 1;
}
}
return 0;
}
- 初始化 ORB。
- 获取 root POA。稍后,我们将创建一个服务程序对象并将其激活(即插入)到此 POA 中。
- 访问 root POA 的 POA 管理器。最初,此 POA 管理器处于挂起状态,因此任何传入的请求都会被排队。当服务器的初始化完成后,我们将把此 POA 管理器置于活动状态,以便可以分派传入的请求。
- 获取命名服务的引用,以便在创建服务程序后,我们可以将其对象引用导出到命名服务。
- 动态创建了一个
CryptographicImpl
服务程序对象。 - 调用
servant_to_reference()
为我们提供了服务程序的引用。 - 将引用转换为字符串以显示它。
- 使用
rebind()
操作将服务程序的引用注册到命名服务中。 - ORB 进入事件循环,以便它可以等待传入的请求。
- 服务程序是引用计数的。创建服务程序时,其引用计数初始化为 1。现在我们完成了对服务程序的处理,我们将其引用计数减一,以便 CORBA 运行时系统知道可以安全地删除它。
- 销毁 ORB。
2.6 实现客户端
客户端应用程序执行一些 CORBA 初始化步骤,从命名服务中检索服务器的对象引用,然后进入一个循环,在该循环中调用 encrypt()
和 decrypt()
。当客户端完成时,它调用 shutdown()
请求服务器终止。
#include <iostream>
#include <string>
#include "OB/CORBA.h"
#include "OB/Cosnaming.h"
#include "crypt.h"
using namespace std;
int main(int argc, char** argv)
{
// Declare ORB
CORBA::ORB_var orb;
try {
// Initialize the ORB
1 orb = CORBA::ORB_init(argc, argv);
// Get a reference to the Naming Service
2 CORBA::Object_var rootContextObj =
orb->resolve_initial_references("NameService");
CosNaming::NamingContext_var nc =
CosNaming::NamingContext::_narrow(rootContextObj.in());
CosNaming::Name name;
name.length(1);
name[0].id = (const char *) "CryptographicService";
name[0].kind = (const char *) "";
// Invoke the root context to retrieve the object reference
3 CORBA::Object_var managerObj = nc->resolve(name);
// Narrow the previous object to obtain the correct type
::CaesarAlgorithm_var manager =
4 ::CaesarAlgorithm::_narrow(managerObj.in());
string info_in,exit,dummy;
CORBA::String_var info_out;
::CaesarAlgorithm::charsequence_var inseq;
unsigned long key,shift;
try{
do{
cout << "\nCryptographic service client" << endl;
cout << "----------------------------" << endl;
do{ // Get the cryptographic key
if (cin.fail())
{
cin.clear();
cin >> dummy;
}
cout << "Enter encryption key: ";
cin >> key;
} while (cin.fail());
do{ // Get the shift
if (cin.fail())
{
cin.clear();
cin >> dummy;
}
cout << "Enter a shift: ";
cin >> shift;
} while (cin.fail());
// Used for debug pourposes
//key = 9876453;
//shift = 938372;
getline(cin,dummy); // Get the text to encrypt
cout << "Enter a plain text to encrypt: ";
getline(cin,info_in);
// Invoke first remote method
5 inseq = manager->encrypt
(info_in.c_str(),key,shift);
cout << "------------------------------------------"
<< endl;
cout << "Encrypted text is: "
<< inseq->get_buffer() << endl;
// Invoke second remote method
6 info_out = manager->decrypt(inseq.in(),key,shift);
cout << "Decrypted text is: "
<< info_out.in() << endl;
cout << "-------------------------------------------"
<< endl;
cout << "Exit? (y/n): ";
cin >> exit;
} while (exit!="y");
// Shutdown server message
7 manager->shutdown();
} catch(const std::exception& std_e){
cerr << std_e.what() << endl;
}
}catch(const CORBA::Exception& e) {
// Handles CORBA exceptions
cerr << e << endl;
}
// End CORBA
if (!CORBA::is_nil(orb)){
try{
orb->destroy();
cout << "Ending CORBA..." << endl;
} catch(const CORBA::Exception& e)
{
cout << "orb->destroy failed:" << e << endl;
return 1;
}
}
return 0;
}
- 初始化 ORB。
- 获取命名服务的引用。
- 调用命名服务上的
resolve()
(表示查找)以检索服务器的对象引用。 narrow()
操作本质上是一个类型转换。我们必须将从命名服务收到的对象引用窄化为正确的子类型,以便我们可以调用其上的操作。- 执行
encrypt()
操作的远程调用。 - 执行
decrypt()
操作的远程调用。 - 执行
shutdown()
操作的远程调用。
2.7 运行客户端-服务器应用程序
在实现客户端和服务器之后,就可以将它们连接起来了。因为我们的演示客户端和服务器通过命名服务交换对象引用,所以我们必须确保命名服务(在 Orbacus 中称为 nameserv
)正在运行。我们使用一些命令行选项来告诉命名服务它应该监听的主机和端口。
nameserv -OAhost localhost -OAport 8140
在此之后,我们可以使用命令行选项启动服务器,告知它如何联系命名服务。
server -ORBInitRef NameService=corbaloc:iiop:localhost:8140/NameService

最后,我们可以启动客户端,同样使用命令行选项来告知它如何联系命名服务。
client -ORBInitRef NameService=corbaloc:iiop:localhost:8140/NameService

3 CORBA 的优缺点
CORBA 提供了多项优势。
首先,CORBA 的实现有多种编程语言(如 C、C++、Java、COBOL、Ada、SmallTalk 和 Python、Tcl 和 Perl)和多种操作系统(包括 Windows、UNIX、大型机和嵌入式设备)。并非所有客户端-服务器技术都能做到这一点。例如,微软技术(COM、DCOM 和 .NET)传统上只在 Windows 上可用。Java RMI 可以在多操作系统之间工作,但只能在基于 Java 的应用程序中使用。
其次,在进行远程调用时,CORBA 将参数封组成紧凑的二进制格式以通过网络传输。这种紧凑的格式节省了网络带宽。此外,将参数封组成二进制格式所需的 CPU 开销也很低。相比之下,一些较新的客户端-服务器技术(如 SOAP)在进行远程调用时将参数封成 XML 格式。XML 非常冗长,因此会占用大量带宽;通常至少是等效 CORBA 调用的十倍带宽。此外,当基于 SOAP 的服务器收到请求时,它必须解析 XML 以提取参数数据。这样做需要大量的 CPU 时间。相比之下,在 CORBA 的二进制消息格式中封入和解封参数的速度要快得多。
第三,CORBA 已经存在十多年了,这意味着其技术相当稳定且功能丰富。
当然,CORBA 并不完美。它的主要缺点是 CORBA 的强大功能和灵活性伴随着相对陡峭的学习曲线。一个名为 CORBA Utilities 的开源库为 C++ 和 Java 开发人员减轻了部分学习曲线,但并非全部。
4 进一步阅读
- 您可以在 CORBA 简单解释 中找到对 CORBA 概念的良好概述。
- 有关用 C++ 编写 CORBA 应用程序的详细信息,请阅读 Michi Henning 和 Steve Vinoski 的《高级 CORBA 编程(C++)》。
- 关于理论和基础知识
- George Coulouris、Jean Dollimore 和 Tim Kindberg 的《分布式系统:概念与设计》。
- 西班牙远程教育大学 - UNED - 的书籍和笔记
5 致谢
我要感谢 Progress Software 的 Ciaran McHale 对本文草稿的反馈。