CORBA 程序设计指南(入门)(转载)
关键词: corba 程序设计CORBA 程序设计指南(入门)
Author: 龙湘明
Company: 北京邮电大学国家重点实验室
Date : 2001-2-28
Update : 2006-03-29
这里假设你对CORBA毫无所知。对JAVA略有所知,因为这里使用JAVA作为程序设计语言。学习了本书,你将对CORBA有个初步了解,并能编写一个简单完整的CORBA应用程序。
1.CORBA 简介
CORBA(Common Object Request Broker Architecture)是为了实现分布式计算而引入的。为了说明CORBA在分布计算上有何特点,我们从它与其它几种分布计算技术的比较中进行说明。
与过去的面向过程的RPC(Remote Procedure Call)不同,CORBA是基于面向对象技术的,它能解决远程对象之间的互操作问题。MicroSoft 的DCOM (Distributed Component Object Model)也是解决这一问题的, 但它基于Windows操作系统,尽管到本书编写时,DCOM已有在其他操作系统如Sun Solaris, Digital Unix, IBM MVS 上的实现,但毫无疑问,只有在微软的操作系统上才会实现得更好。而只有CORBA是真正跨平台的,平台独立性正是CORBA的初衷之一。另一种做到平台无关性的技术是Java RMI(Remote Method Invocation),但它只能用JAVA实现。CORBA与此不同,它通过一种叫IDL(Interface Definition Language)的接口定义语言,能做到语言无关,也就是说,任何语言都能制作CORBA组件,而CORBA组件能在任何语言下使用。
因此,可以这样理解CORBA:CORBA一种异构平台下的语言无关的对象互操作模型。
1.1 CORBA体系结构
CORBA的体系结构如下:
图1.1 CORBA体系结构
CORBA上的服务用IDL描述,IDL将被映射为某种程序设计语言如C++或Java,并且分成两分,在客户方叫IDL Stub(桩), 在服务器方叫IDL Skeleton(骨架)。两者可以采用不同的语言。服务器方在Skeleton的基础上编写对象实现(Object Implementation),而客户方要访问服务器对象上的方法,则要通过客户桩。而双方又要通过而ORB(Object Request Broker,对象请求代理)总线通信。
与传统的Client/Server模式(我们称为Two-tier client/server)不同,CORBA是一种multi-tier client/server architecture,更确切的说,是一种three-tier client/server模式。双重客户/服务器模式存在的问题是两者耦合太紧,它们之间采用一种私有协议通信,服务器的改变将影响到客户方。多重客户/服务器与此不同,两者之间的通信不能直接进行,而需要通过中间的一种叫代理的方式进行。在CORBA中这种代理就是ORB。通过它,客户和服务器不再关心通信问题,它们只需关心功能上的实现。从这个意义上讲,CORBA是一种中间件(Middleware)技术。
下面列出CORBA中的一些重要概念,或者说CORBA中的几个重要名词,有助于读者了解CORBA的一些重要的方面。
1.2 CORBA中的几个概念
1.2.1 ORB(Object Request Broker)
CORBA体系结构的核心就是ORB。可以这样简单理解:ORB就是使得客户应用程序能调用远端对象方法的一种机制。
图1.2 ORB模型
具体来说就是:当客户程序要调用远程对象上的方法时,首先要得到这个远程对象的引用,之后就可以像调用本地方法一样调用远程对象的方法。当发出一个调用时,实际上ORB会截取这个调用(通过客户Stub完成,“提高”篇中会详细解释),因为客户和服务器可能在不同的网络、不同的操作系统上甚至用不同的语言实现,ORB还要负责将调用的名字、参数等编码成标准的方式(称Marshaling)通过网络传输到服务器方(实际上在同一台机器上也如此),并通过将参数Unmarshaling的过程,传到正确的对象上(这整个过程叫重定向,Redirecting),服务器对象完成处理后,ORB通过同样的Marshaling/Unmarshaling方式将结果返回给客户。
因此,ORB是一种功能,它具备以下能力:
1.对象定位(根据对象引用定位对象的实现)
2.对象定位后,确信Server能接受请求
3.将客户方请求通过Marshaling/Unmarshing方式重定向到服务器对象上
4.如果需要,将结果以同样的方式返回。
1.2.2 IDL(Interface Definition Language)
IDL,接口定义语言,是CORBA体系中的另一个重要概念。如果说ORB使CORBA做到平台无关,那么IDL, 则使CORBA做到语言无关。
正像其名字中显示的那样,IDL仅仅定义接口,而不定义实现,类似于C中的头文件。实际上它不是真正的编程语言。要用它编写应用,需要将它映射它相应的程序设计语言上去,如映射到C++或JAVA上去。映射后的代码叫Client Stub Code和 Server Skeleton Code。
IDL的好处是使高层设计人员不必考虑实现细节而只需关心功能描述。IDL可以说是描述性语言。设计IDL的过程也是设计对象模型的过程。它是编写CORBA应用的第一步,在整个软件设计过程中至关重要。
IDL的语法很像C++,当然也像Java。很难想像一个程序设计人员是不懂C或Java的,所以,几乎所有的程序设计人员都能迅速理解IDL。而这正是IDL设计者所希望的。
下面是一个IDL定义的简单例子:
// grid.idl
// IDL definition of a 2-D grid:
module simpleDemo{
interface grid {
readonly attribute short height; // height of the grid
readonly attribute short width; // width of the grid
// IDL operations
// set the element [row,col] of the grid, to value:
void set(in short row, in short col, in long value);
// return element [row,col] of the grid:
long get(in short row, in short col);
};
};
This IDL defines an interface for a grid
CORBA object that maintains a grid or 2-D array of data values, which a client can access or modify remotely.
Module类似于Java中包(Package)的概念,实际上module simpleDemo映射到JAVA正是package simpleDemo。而Interface类似于C++中的类(classs)声明,或是Java中的Interface 定义。
附录中列出了IDL的全部语法。
1.2.3 Stub Code 和 Skeleton Code
Stub code 和Skeleton Code 是由IDL Complier自动生成的,前者放在客户方,后者放在服务器方。不同厂商的IDL complier生成的Stub 和Skeleton会略有区别,但影响不大。
如上面的grid.idl, 编译后,Stub Code包含以下文件:
grid.java
_gridStub.java
gridHelper.java
gridHolder.java
gridOperations.java
Skeleton Code则包含以下文件:
gridOperations.java
gridPOA.java
gridPOATie.java
(在Stud Code 也包含gridOperations.java, 是因为在使用Call back机制时会用到。)
这些文件的用途后面会讲到。
1.2.4 GIOP 和 IIOP
我们知道,客户和服务器是通过ORB交互的,那么,客户方的ORB和服务器方的ORB又是通过什么方式通信呢?通过GIOP(General Inter-ORB Protocol)。也就是说,GIOP是一种通信协议,它规定了两个实体:客户和服务器ORBs间的通信机制。
图1.3 ORBs通信机制
GIOP在设计时遵循以下目标:
Ø Widest possible availability
Ø Simplicity
Ø Scalability
Ø Low cost
Ø Generality
Ø Architectural neutrality
也是说,GIOP设计的尽可能简单,开销最小,同时又具有最广泛的适应性和可扩展性,以适应不同的网络。
GIOP定义了以下几个方面:
1.The Common Data Representation (CDR) definition.
通用数据表示定义。它实际上是IDL数据类型在网上传输时的编码方案。它对所有IDL数据类型的映射都作了规定。
2.GIOP Message Formats.
它规定了Client和Server两个角色之间要传输的消息格式。主要包括Request和Reply两种消息。
一个Request 消息有以下几部分组成:
A GIOP message header
A Request Header
The Request Body
相应的,一个Reply消息则包括
A GIOP message header
A Reply Header
The Reply Body
GIOP1.1规定 GIOP message header格式如下:
// GIOP 1.1
struct MessageHeader_1_1 {
char magic [4];
Version GIOP_version;
octet flags; // GIOP 1.1 change
octet message_type;
unsigned long message_size;
};
Request Header格式如下:
// GIOP 1.1
struct RequestHeader_1_1 {
IOP::ServiceContextList service_context;
unsigned long request_id;
boolean response_expected;
octet reserved[3]; // Added in GIOP 1.1
sequence object_key;
string operation;
Principal requesting_principal;
};
Request Body则按CDR规定的方式编码,它主要对方法调用的参数进行编码, 如方法:
double example (in short m, inout Principal p);
可表示成:
struct example_body {
short m; // leftmost in or inout parameter
Principal p; // ... to the rightmost
};
3.GIOP Transport Assumptions:
主要规定在任何面向连接的网络传输层上的一些操作规则。如:Asymmetrical connection usage,Request multiplexing,Overlapping requests,Connection management等。
另外,因为CORBA是基于对象的,GIOP还需定义一套Object Location的机制。
GIOP因为是一种通用协议,所以不能直接使用。在不同的网络上需要有不同的实现。目前使用最广的便是Internet上的GIOP,称为IIOP(Internet Inter-ORB Protocol)。IIOP基于TCP/IP协议。IIOP消息格式定义如下:
module IIOP { // IDL extended for version 1.1
struct Version {
octet major;
octet minor;
};
struct ProfileBody_1_0 { // renamed from ProfileBody
Version iiop_version;
string host;
unsigned short port;
sequence object_key;
};
struct ProfileBody_1_1 {
Version iiop_version;
string host;
unsigned short port;
sequence object_key;
sequence <:taggedcomponent>components;
};
};
1.2.5 Dynamic Invocation Interface (DII) 和 Dynamic Skeleton Interface (DSI)
动态调用接口(DII)和动态骨架接口(DSI)是用来支持客户在不知道服务器对象的接口的情况下也能调用服务器对象。
一个增加了DII和DSI的CORBA调用模型如下:
图1.4 增加了DII和DSI的调用模型
我们在“提高篇”中再详细讲述DII和DSI。
1.2.6 Object Adapter (对象适配器)
对象适配器是ORB的一部分。它主要完成对象引用的生成、维护,对象定位等功能。对象适配器有各种各样。Basic Object Adapter(BOA, 基本对象适配器)实现了对象适配器的一些核心功能。而Portable Object Adapter(POA,可移植对象适配器)则力图解决对象实现在不同厂商的ORBs下也能使用的问题。最新的ORB产品一般都支持POA。
还有其他一些专有领域的对象适配器如Database Object Adapter等。
1.3 CORBA历史
CORBA是由OMG(Object Management Group)负责制定和维护的一组规范。OMG成立于1989年,是一个非营利的国际性软件组织,主要致力于为分布式计算提供解决方案并制定规范。除CORBA外,OMG还制定了如UML(United Modeling Language, 统一建模语言)、CWM等其他重要规范。OMG目前已有世界上760多个成员,东南大学是中国唯一的OMG成员。
CORBA自1990提出并被OMG采纳以来,已历经多个版本。分别称为 CORBA 1、CORBA 2和CORBA 3。其中CORBA 1是对CORBA 1.x的统称,CORBA 2是对CORBA 2.x的统称。目前CORBA 3规范还在制订中,不久便可面世。
下面是CORBA版本的更新历史。
CORBA1 :
CORBA 1.0 90-12, 提出CORBA框架
CORBA 1.1 91早期,定义了IDL及ORB Interface。
CORBA 1.2 92-93, 修订
CORBA定义了ORB的一些基本特性,但是没有定义而ORB间的通用协议。CORBA 2主要解决这一问题。
CORBA 2:
CORBA 2.0 94-12, 定义了GIOP和IIOP
CORBA 2.1 97-08
CORBA 2.2 98-02, 增加了POA。
CORBA 2.3 98-12
CORBA 2.3.1 99-10, 一些Change bars.
CORBA 2.4 00-10, 具备CORBA 3的雏形,包括:QoS Specification, Asynchronous Messaging, Minimum CORBA, Real-Time CORBA, CORBA Components, Notification Services, Firewall Specification等。
CORBA 2.4.1 00-11
CORBA 2.4.2 01-02
因此,可以说,CORBA 1提出ORB和IDL等CORBA基本概念,CORBA 2主要贡献是IIOP,而在制订中CORBA 3 则集中于CORBA Components技术及Real-time特性等。
对CORBA的简要介绍就到这里。关于OMG及CORBA的更多信息,可访问OMG站点:
下面我们开始编写第一个CORBA程序。
2.2 编写第一个CORBA程序
2.2.1 命令行方式
我们先介绍在命令行方式下的开发步骤,编写一些小型的测试程序可以使用这种方式,
因为没有必要启动庞大的JBuilder 4。 后面我们再介绍在JBuilder 4集成开发环境下的使用方法。实际上两者步骤是类似的,只是方便程度不一样。
通常,开发一个CORBA应用的大致步骤如下:
1)定义 IDL。
2)用IDL complier 将IDL编译成 Client Stub 代码和 Server Skeleton代码。
3)编写Server方的实现(Implementation)代码。
4)编写 Server。
5)编写 Client。
6)编译和调试。
7)运行程序。
编写Server Implementation和编写Server不是一回事。Server和Client都是带有main()函数的可执行的程序。但Server Implementation 是对interface的实现,它可以单独放在一个.java文件中,也可以和Server放在同一个文件中。
下面以一个简单的例子详细讲解各个步骤。
第一步:定义IDL
首先建立一个工作目录,这里假设为D:/mywork/simpleDemo。
然后编写IDL。这个例子我们使用1.2.1 节中grid.idl,一个对二维数组操作的例子。可以使用任何文本编辑器编写,如UltraEdit等。之后保存到D:/mywork/simpleDemo下。
grid.idl定义了一个接口interface grid。其中定义两个方法get()和set(),用以获取和设置数组元素值。注意属性(attribute)的定义,这是C和JAVA中没有的概念(在Delphi中有这个概念),实际上它就相当于类的数据定义,但在映射到Java时被映射成方法。
第二步:编译IDL 为Java Stub和Skeleton.
打开DOS窗口,进入到D:/mywork/simpleDemo目录,在Dos命令提示符下键入:
idlj grid.idl
idlj –fserver grid.idl
注意,
我们看看idl生成的stub和skeleton代码。
Stub Code包含以下文件:
grid.java 定义了接口 interface grid,
public interface grid
extends gridOperations,
org.omg.CORBA.Object,
org.omg.CORBA.portable.IDLEntity
{
}
直接由grid.idl中的interface grid 映射而来。
_gridStub.java 桩代码,定义了class _gridStub
public class _gridStub
extends org.omg.CORBA.portable.ObjectImpl
implements grid{
public void set(){…} ;
pubic int get(){..};
…}
class _gridStub中实现了客户方的set()和get()等方法,它们封装了ORB的功能,截取客户的调用,执行Marshaling/Unmarshing等操作,最终得到调用结果。也就是说,在客户方声明一个grid类型的对象,实际上使用gridHelper操作得到的是一个_gridStub类型的实例,调用grid上的方法实际上是调用_gridStub上的方法。
gridHelper.java 定义 public class gridHelper{ }
帮助客户方grid对象生成一个_gridStub类型的实例,同时得到服务器对象的引用。
gridHolder.java 定义public final class gridHolder
implements org.omg.CORBA.portable.Streamable{}
当需要处理out,inout类型的参数时,需要这个类。
gridOperations.java 定义public interface gridOperations{
void set()
int get() }
这个接口实际上是Server使用的。当客户方需要使用callback功能时,也需要这个接口。
Skeleton Code则包含以下文件:
gridPOA.java 定义类abstract public class gridPOA
extends org.omg.PortableServer.Servant
implements org.omg.CORBA.portable.InvokeHandler, gridOperations
{
}
Server方的Implementation代码直接继承自这个类。
gridPOATie.java 定义类public class gridPOATie
extends simpleDemo.gridPOA
{
}
当使用代理方式而不是继承方式创建Server Object时,使用这个类。
-jpoa 参数只生成上述两个文件,实际上Skeleton Code也包含gridOperations.java,因为class gridPOA就是实现gridOperations的。
上述接口(类)的继承关系如下:
grid.idl中只定义了一个interface grid,实际上如果定义了多个interface, 那么对每个interface都会生成和上面类似的一套代码。
第三步:编写Server方的Implementation代码(即编写Servant)
到现在为止,我们才开始真正编写代码。
引入了概念后,Server方的实现对象称为Servant, 编写Implementation代码实际上就是对IDL定义的每个interface,都编写一个Servant,其中要现实interface中定义的每个方法。.
这里我们将Servant类定义为class gridImpl,类名字可以自己随便取,通常都命名为xxxImpl的形式。Class gridImpl直接继承自gridPOA。
在D:/mywork/simpleDemo目录下创建一个gridImpl.java文件,内容如下:
package simpleDemo;
public class gridImpl extends gridPOA{
short m_height; // store the height
short m_width; // store the width
int m_array[][]; // a 2D array to hold the grid data
private int i=0;
private int j=0;
private int n=0;
public gridImpl(short width, short height)
{
m_array = new int[width][height]; // allocate the 2D array
m_height = height; // set up height
m_width = width; // set up width
}
/*
* Implementation of the method which reads the height attribute
*/
public short height()
{
return m_height;
}
/*
* Implementation of the method which reads the width attribute
*/
public short width()
{
return m_width;
}
/*
* Implementation of the set operation
*/
public void set (short x, short y, int value)
{
i++;
n++;
System.out.println ("-----µÚ "+n+" ´Îµ÷Óà Server / µÚ "+i+" ´Îµ÷Óà grid.set()----");
System.out.println ("In grid.set() x = " + x);
System.out.println ("In grid.set() y = " + y);
System.out.println ("In grid.set() value = " + value);
m_array[y][x] = value;
}
/*
* Implementation of the get operation
*/
public int get (short x, short y)
{
j++;
n++;
System.out.println ("-----µÚ "+n+" ´Îµ÷Óà Server / µÚ "+j+" ´Îµ÷ÓÃ: grid.get()----");
int value = m_array[y][x];
System.out.println ("In grid.get() x = " + x);
System.out.println ("In grid.get() y = " + y);
System.out.println ("In grid.get() value = " + value);
return value;
}
}
编写好Implementation代码后,实际上你就可以编译了。使用it_javac 命令进行编译。在编译gridImpl.java之前,请先编译idl生成的stub和skeleton代码。
1) 转到D:/mywork/simpleDemo,用mkdir classes建立一个目录d:/mywork/simpleDeom/classes, 我们编译后的class文件都放在这个目录下。
第四步:编写Server
Servant仅是实现代码,而Server是包含main()函数的可执行的代码。Server的主要任务就是创建所需的Servant,同时通知POA已准备就绪,可以接受客户方的请求。
新建一个文件gridServer.java,放在D:/mywork/simpleDemo下,代码如下:
package simpleDemo;
import java.io.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
public class gridServer {
public static ORB global_orb;
public static void main(String[] args) {
final short width = 100;
final short height = 100;
// 1. Initalise ORB for an application and resolve RootPOA reference
try{
System.out.println("Initializing the ORB");
global_orb = ORB.init (args,null);
org.omg.CORBA.Object poa_obj = global_orb.resolve_initial_references("RootPOA");
&bsp; org.omg.PortableServer.POA root_poa = org.omg.PortableServer.POAHelper.narrow(poa_obj);
// 2. Create servants, activate them, write their IORs to files.
//
System.out.println("Creating objects");
gridImpl grid=new gridImpl(width,height);
byte[] grid_oid = root_poa.activate_object(grid);
export_object(root_poa, grid_oid, "../grid.ior", gridHelper.id());
// 3. Activate the POA Manager to allow new requests to arrive
//
System.out.println("Activating the POA Manager");
org.omg.PortableServer.POAManager poa_manager = root_poa.the_POAManager();
poa_manager.activate();
// 4. Give control to the ORB to let it process incoming requests
//
System.out.println("Giving control to the ORB to process requests");
global_orb.run();
System.out.println ("Done");
}
catch(org.omg.CORBA.ORBPackage.InvalidName ex)
{
System.out.println("Failed to obtain root poa " + ex );
}
catch(org.omg.PortableServer.POAPackage.WrongPolicy ex)
{
System.out.println("Invalid POA policy " + ex );
}
catch(org.omg.PortableServer.POAPackage.ServantAlreadyActive ex)
{
System.out.println("POA servant already active " + ex );
}
catch(org.omg.PortableServer.POAManagerPackage.AdapterInactive ex)
{
System.out.println("POAManager activate failed" + ex );
}
System.out.println("gridServer exiting...");
}
/**
* This function takes a poa and object id, builds an object
* reference representing that object, and exports the object
* reference to a file.
*/
public static void export_object(POA poa, byte[] oid, String filename, String type_id)
{
try
{
org.omg.CORBA.Object ref = poa.create_reference_with_id(oid, type_id);
String stringified_ref = global_orb.object_to_string(ref);
RandomAccessFile FileStream = null;
try
{
FileStream = new RandomAccessFile(filename,"rw");
FileStream.writeBytes(stringified_ref);
FileStream.close();
}
catch(Exception ex)
{
System.out.println("Failed to write to " + filename );
}
}
catch(org.omg.PortableServer.POAPackage.WrongPolicy ex)
{
System.out.println("Invalid POA policy " + ex );
}
}
}
通常,一个Server主程序执行以下几个步骤:
1) 初始化ORB;
2) 创建Servant对象;
3) 激活POA管理器;
4) 通知ORB准备接受请求。
只有第二步是需要用户编写的,其他步骤所有Server程序都大致一样。在创建Servant对象后还需注册其引用,这里我们使用了将其引用保留到文件中的方法(file based)。方法export_object()用来完成这一个工作,这是我们自己定义的,实际上可以作为一个通用方法使用在所有Server程序中使用。当客户方
Orbix 2000提供了两种获取对象引用的方式,一种叫file based,即读取文件的方式;一种叫Naming Service,即通常所说的命名服务。前一种方式的好处是不需要启动ORB守护进程,只需启动Server和Client两个程序就可以了。当Client需要使用获得Servant的引用时,需要从文件中读取,所以实际上当Client和Server在同一台机器上时才是可行的,在真正跨机器、跨平台的分布式应用中,通常使用Naming Servic,它的不足是需要启动Naming Service守护进程,这对其健壮性要求很高,可能成为系统瓶颈。
第五步:编写Client
Client程序就是客户方的可执行程序,它需要使用到Server方的服务。
新建一个文件gridClient.java,保存到D:/mywork/simpleDemo,源代码如下:
package simpleDemo;
import java.io.*;
import org.omg.CORBA.*;
/*
* The javaclient will connect to the Grid server and invoke
* operations enabling it to manipulate a 2 dimensional array.
*/
public class gridClient
{
public static void main (String args[])
{
System.out.println ("The Client begin.../n");
ORB orb=null;
grid gridProxy = null;
grid gridProxy2=null;
org.omg.CORBA.Object obj_ref=null;
/*
* Initalise the ORB for an Orbix application.
*/
try {
orb=ORB.init (args,null);
/*
* Establish a CORBA connection with the grid server and
* return a proxy for it.
*/
obj_ref=import_object(orb,"../grid.ior");
gridProxy = gridHelper.narrow (obj_ref);
gridProxy2 = gridHelper.narrow(obj_ref);
/*
* Get the width and height by calling the remote
* width and height operations on the grid server.
*/
short width = 0;
short height = 0;
/*
* Set some values in the remote grid.
*/
short x_coord = 2;
short y_coord = 4;
try {
gridProxy.set (x_coord, y_coord, 123);
gridProxy2.set (x_coord, y_coord, 111);
}
catch (SystemException ex) {
System.out.println ("FAIL/tException setting values in the grid.");
System.out.println (ex.toString());
System.exit (1);
}
/*
* Check the values placed in the grid
* were set correctly.
*/
int val = 0,val2=0;
x_coord = 2; y_coord = 4;
val = gridProxy.get (x_coord, y_coord);
val2 = gridProxy2.get (x_coord, y_coord);
System.out.println ("Value for grid[2,4] is " + val);
System.out.println ("Value2 for grid[2,4] is " + val2);
}
catch (org.omg.CORBA.SystemException ex)
{
System.err.println(ex);
System.exit(ex.minor);
}
catch (Exception ex)
{
System.err.println(ex);
System.exit(1);
}
finally
{
if (orb != null)
{
try { orb.shutdown(true); } catch (Exception ex) { /* do nothing */ }
}
}
System.out.println ("/nGrid demo finished.");
}
public static org.omg.CORBA.Object import_object(ORB orb, String filename)
{
String ior = null;
RandomAccessFile FileStream = null;
System.out.println("Reading object reference from " + filename);
try
{
FileStream = new RandomAccessFile(filename,"r");
ior = FileStream.readLine();
return orb.string_to_object(ior);
}
catch(Exception e)
{
System.out.println("Error opening file " + filename);
return null;
}
}
}
通常,客户程序需要执行以下步骤:
1) 初始化ORB;
2) 获得Servant对象的引用;
3) 调用其上的操作
这里使用了读取文件的方法获得对象引用,自定义的方法import_object()用来完成这里任务。
这个程序生成了两个客户对象:gridProxy和gridProxy2, 只是为了说明当使用同一个引用的时候,尽管是多个客户对象,但操作的是同一个Servant对象的数据。