ICE通信中客户与服务器的结构

参考《Ice分布式程序设计》马维达 译

一、客户与服务器的结构

    Ice 客户与服务器内部的逻辑结构如图所示:

    客户与服务器都由这样一些代码混合而成:应用代码、库代码、根据Slice 定义生成的代码:

Ice 核心为远地通信提供了客户端和服务器端运行时支持。其中的大量代码所涉及的是网络通信、线程、字节序,以及其他许多与网络有关的问题,我们应用代码应该与这些问题隔离开来。

代理代码是根据你的Slice 定义生成的如果你调用代理的某个函数,就会有一个RPC 消息被发给服务器,调用服务端目标对象上的某个对应的函数。

    在上文中提到,如下客户代码中,PrinterPrx代理类是由slice2java命令编译printer.ice文件生成的,

 

Ice.ObjectPrx base = ic
					.stringToProxy("SimplePrinter:default -p 10000");
PrinterPrx printer = PrinterPrxHelper.checkedCast(base);
if (printer == null)
	throw new Error("Invalid proxy");
printer.printString("Hello World!");

    当调用printer代理中的printString方法,客户会发送RPC消息,调用服务器上PrinterI类对象中printString方法。

    要想与某个Ice 对象联系,客户必须持有这个对象的代理。 对客户而言,代理就是Ice 对象的代表(该对象可能在远地)。一个代理充当的是一个Ice 对象的本地大使,客户端中PrinterPrx代理对象就是PrinterI类在客户端的本地的大使。

    当客户调用代理上的操作时,Ice run time 会:

1. 定位Ice 对象

2. 如果Ice 对象的服务器没有运行,就激活它

3. 在服务器中激活Ice 对象

4. 把所有in 参数传送给Ice 对象

5. 等待操作完成

6. 把所有out 参数及返回值返回给客户(或在发生错误的情况下抛出异常)代理封装了完成这一系列步骤所必需的全部信息。特别地,代理包含有:

寻址信息:用于让客户端run time 联系正确的服务器

对象标识:用于确定服务器中的哪一个对象是请求的目标

可选的facet 标识符:用于确定代理所引用的是对象的哪一个facet

骨架代码也是根据你的Slice 定义生成的,因此,与你用Slice 定义的对象和数据的类型是对应的。骨架代码是客户端代理代码的服务器端等价物:它提供了向上调用接口,允许Ice runtime 把控制线程转交给你编写的应用代码。

对象适配器是专用于服务器端的Ice API 的一部分:只有服务器才使用对象适配器。对象适配器有若干功能:

    1、对象适配器把来自客户的请求映射到servant对象上的特定方法。换句话说,对象适配器会跟踪在内存中,都有哪些servant,其对象标识又是什么。对象适配器与一个或多个传输端点关联在一起。

    servant就是服务器开发者编写的类,客户端发送的请求,最终调用的是servant实例上的方法。

如上文中的服务端代码片段,

Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints(
					"SimplePrinterAdapter", "default -p 10000");

点“default -p 10000与adapter适配器绑定,

串“SimplePrinterAdapter”代表适配器的名称。

    如果与某个适配器关联的传输端点不止一个,你可以通过多种传输机制到达在该适配器中的servant。例如,为了提供不同的服务质量和性能,你可以把一个TCP/IP 端点和一个UDP 端点与一个适配器关联在一起。

    2、对象适配器要负责创建可以传给客户的代理。对象适配器知道它的每个对象的类型、标识,以及传输机制的详细情况,并且会在服务器端应用代码要求创建代理时在其中嵌入正确的信息。

    每个对象适配器都维护有一个叫作活动servant 映射表(active servant map)的数据结构。活动servant 映射表(简称为ASM)是一个查找表,于把对象标识映射到servant

    当客户把操作调用发给服务器时,请求的目标是特定的传输端点。传输端点隐含地标识了请求所针对的对象适配器(因为同一个端点只能绑定到一个对象适配器)。客户藉以发送请求的代理含有对应的对象的标识,客户端run time 会在线路上随调用一起发送这个对象标识。对象适配器继而使用这个对象标识、在它的ASM 中查找正确的servant,把调用分派给它。

如上文中的服务端代码片段:

Ice.Object object = new PrinterI();
			//将服务单元增加到适配器中,并给服务对象指定名称为SimplePrinter,该名称用于唯一确定一个服务单元 
			adapter.add(object, Ice.Util.stringToIdentity("SimplePrinter"));

Add方法将object对象注册到adapter适配器中,并使用串SimplePrinter创建一个对象标,这样在适配器的ASM表中就有了一个以串“SimplePrinter”标识的servant对象object

在客户端代码中,使用串"SimplePrinter:default -p 10000"创建PrinterI在客户端的代理对象,串default -p 10000即代表适配器,串SimplePrinter即为ASM表中的对象标识,当客户端使用代理对象调用PrinterI上的printString方法时,就可以在ASM中定位到对应的对象。

二、SliceIce 规范语言)简介

每个Ice 对象都有一个接口,该接口具有一些操作。接口、操作,还有在客户及服务器间交换的数据的类型,都是用Slice 语言定义的。

如上文中的printer.ice文件:

module demo {

  interface Printer {

    void printString(string s);

  };

};

Slice 允许你以一种独立于特定编程语言(比如C++ Java)的方式定义客户-服务器的合约。 Slice 定义由一个编译器编译成特定编程语言的API,也就是说,与你所定义的接口和类型对应的那一部分API,会由生成的代码组成。

1. 客户和服务器使用相同的开发环境

    编写任何Ice 应用的第一步都是要编写一个Slice 定义,其中含有应用所用的各个接口。

    下图说明客户和服务器都用C++ 开发的情况。Slice 编译器根据源文件Printer.ice 中的Slice 定义生成了两个文件: 一个头文件(Printer.h)和一个源文件(Printer.cpp)。

    下图说明客户和服务器都用java开发的情况。Slice 编译器根据源文件Printer.ice中的Slice 定义生成了若干个文件这些主要分成两部分,一部分是客户端程序依赖的类文件,一部分是服务端程序依赖的类文件

2. 客户和服务器使用不同的开发环境

    如果客户和服务器是用不同的语言开发的,它们不能共享任何源文件或二进制组件。例如,用Java编写的客户不能包括C++头文件。

    下图说明客户用Java编写而对应的服务器用C++编写的情况。在这种情况下,客户和服务器开发者是完全独立的,分别使用自己的开发环境和语言映射。客户和服务器开发者之间的唯一链接是它们各自使用的Slice 定义。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值