Ice中间件学习 02 【容易实现HelloWorld】

Ice中间件学习 02 【简单实现HelloWorld】

注:该部分内容部分来自网络,如有侵权请联系

一、简述

创建一个非常简单的客户-服务器Ice应用,这个应用提供远程地打印功能:客户端发送要打印的文本给服务器,再由服务器把文本发给打印机,打印程序只是把文本打印到终端,而不是真正的打印机,目的是说明客户怎样与服务器通信。

二、安装Ice

Ice官方下载:http://www.zeroc.com/download.html

2.1、安装Ice(过程略)

2.2、配置路径

配置环境变量:将Ice的安装路径下的bin目录配置到系统的path环境变量中。如下图:

Ice中间件学习 02 【容易实现HelloWorld】

-1

测试安装是否成功,只需要在安装的机器上打开cmd输入slice2java命令即可(该系列所有文章针对Java语言实现),如下图所示:

Ice中间件学习 02 【容易实现HelloWorld】

图-2

说明Ice配置完成!

三、HelloWorld实现

3.1、使用MyEclipse开发,创建一个项目为iceTest的Java工程,在src

Ice中间件学习 02 【容易实现HelloWorld】

3.2、编写Slice定义

编写任何Ice应用的第一个步骤就是要编写一个Slice定义,其中包含应用所用的各个接口,我们为打印应用编写了如下这样的Slice定义:

module demo{
	interface Printer{
		void printString(string s);
	};
};

我们把这段代码片段保存在叫做printer.ice的文件中,存放在src目录下,如下图所示

Ice中间件学习 02 【容易实现HelloWorld】

-3

Slice定义含有一个接口,叫做Printer,该接口非常简单,只提供了一个操作叫做printString,printString操作接收一个串作为它唯一的输入参数;这个串的文本将会出现在打印机上。

3.3、编写使用Java的Ice应用

3.3.1、编译printer.ice

在cmd命令中进入iceTest工程中printer.ice文件所在的目录下,如下图所示:

Ice中间件学习 02 【容易实现HelloWorld】

-4

cmd中输入命令:slice2java printer.ice

Ice中间件学习 02 【容易实现HelloWorld】

-5

说明:slice2java编译器根据这个printer.ice文件定义生成一些Java源文件,我们目前无需关注这些文件的确切内容,它们所包含的是编译器生成的代码,与我们在printer.ice中定义的Printer接口相对应。

此时刷新iceTest工程,可以看到多出了很多的文件,之所以编译以后生成的Java文件存放在demo下,是因为我们的Printer.ice文件中使用module demo定义了包路径。

Ice中间件学习 02 【容易实现HelloWorld】

-6

说明:demo包下的所有Java文件都报错,这是因为没有导入Ice运行所需要的jar包,我们在Ice的安装目录下的lib包找到ice.jar,将该jar包导入到iceTest工程下即可。

Ice中间件学习 02 【容易实现HelloWorld】

图-7

3.3.2、编写和编译服务器

要实现Printer接口,必须创建一个servant类,按照惯例,servant类的名字是接口的名字加上一个I后缀,所以servant类叫做PrintI,并放在PrinterI.java源文件中,存放在com.xuz.servant包中。

package com.xuz.servant;
import Ice.Current;
import demo._PrinterDisp;
public class PrintI extends _PrinterDisp {
	@Override
	public void printString(String s, Current current) {
		System.out.println(s);
	}
}

说明:PrintI类继承自叫做_PrinterDisp的基类,该类由slice2java编译器生成,是一个抽象类,其中含有一个printString方法,其参数时打印机要打印的串,以及类型为Ice.Current的对象,我们的printString方法的实现会简单地把它的参数写到终端,服务器代码其余部分在一个叫做Server.java的文件中,存在com.xuz.server包中,下面给出了其完整的代码:

package com.xuz.server;
import com.xuz.servant.PrintI;
public class Server {
	public static void main(String[] args) {
		int status = 0;
		Ice.Communicator ic = null;
		try {
			//初始化连接,args可以传一些初始化参数,如连接超时,初始化客户端连接池的数量等
			ic = Ice.Util.initialize(args);
			//创建名为SimplePrinterAdapter的适配器,并要求适配器使用缺省的协议(TCP/IP 端口为10000的请求)
			Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000");
			//实例化一个Printer对象,为Printer接口创建一个服务对象
			Ice.Object object = new PrintI();
			//将服务单元增加到适配器中,并给服务对象指定名称为SimplePrinter,该名称用于唯一确定一个服务单元
			adapter.add(object, Ice.Util.stringToIdentity("SimplePrinter"));
			//激活适配器
			adapter.activate();
			//让服务在退出之前,一直持续对请求的监听
			ic.waitForShutdown();
		} catch (Exception e) {
			e.printStackTrace();
			status = 1;
		}finally{
			if(ic!=null){
				ic.destroy();
			}
		}
		System.exit(status);
	}
}

说明:main的主题含有一个try块,把所有的服务器代码都放在其中;这段代码会在退出之前销毁通信器(如果曾今成功创建过),要是Ice run time正常结束,这样做是必须的:程序必须调用它所创建的任何通信器的destroy;否则就会产生不确定的行为。我们把对destory的调用放进finally块,这样不管前面的try块中发生什么异常,通信器都保证会正确销毁。

try块中主体含有实际的服务器代码:

//初始化连接,args可以传一些初始化参数,如连接超时,初始化客户端连接池的数量等
			ic = Ice.Util.initialize(args);
			//创建名为SimplePrinterAdapter的适配器,并要求适配器使用缺省的协议(TCP/IP 端口为10000的请求)
			Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000");
			//实例化一个Printer对象,为Printer接口创建一个服务对象
			Ice.Object object = new PrintI();
			//将服务单元增加到适配器中,并给服务对象指定名称为SimplePrinter,该名称用于唯一确定一个服务单元
			adapter.add(object, Ice.Util.stringToIdentity("SimplePrinter"));
			//激活适配器
			adapter.activate();
			//让服务在退出之前,一直持续对请求的监听
			ic.waitForShutdown();

这段代码包含了一下步骤:
1)、我们调用Ice.Util.initialize初始化Ice run time(之所以把args传给这个调用,是因为服务器可能有run time 感兴趣的命令行参数;就这个例子而言,服务器不需要任何命令参数)。对initialize的调用返回的是一个通信器Ice.Communicator引用,这个引用是Ice run time的主句柄。
2)、调用通信Communicator实例上的createObjectAdapterWithEndpoints,创建一个对象适配器,传入的参数是:SimplePrinterAdapter(适配器的名字)。
3)、这时服务器run time已经初始化,实例化一个PrintI对象,为我们的Printer接口创建一个servant。
4)、调用适配器add,告诉它有了一个新的servant;传给add的参数时我们刚才实例化的servant,再加上一个标示符,在这里"SimplePrinter"串是servant的名字(如果有多个打印机,每个打印机都有不同的名字,更正确的说法是,都会有不同的标示符)。
5)、接下来,调用适配器的activate方法激活适配器(适配器一开始是在holding状态创建,如果适配器处于holding状态,服务器端run time就会停止从对应的传输端点读取数据,不接受客户端发送的连接请求,这种做法在下面这样的情况下很有用:如果有很多歌servant,它们共享同一个适配器,而在所有servant实例化之前我们不想处理请求)。一旦适配器被激活,服务器就会开始处理来自客户端的请求。
6)、最后调用waitForShutdown。这个方法挂起发出调用的线程,直到服务器实现终止为止-或者是通过发出的一个调用关闭run time,或者是对某个信号做出相应(目前,当我们不再需要服务器时,会简单地在命令行上终端它)。
注意:尽管这里的代码不算少,但它们对所有的服务器都是一样的,你可以把这些代码放在一个辅助类里,然后就无需再为它费心了(Ice提供了一个辅助类:Ice.Application)。就实际的应用代码而言,服务器只有几行代码:六行代码定义PrintI类,再加上三、四行代码实例化一个PrintI对象,并向对象适配器注册它。

3.3.3、编写和编译客户端
客户端代码在Client.java中,看起来与服务器非常类似,存放在com.xuz.client包中,代码如下:

package com.xuz.client;
import demo.PrinterPrx;
import demo.PrinterPrxHelper;
public class Client {
	public static void main(String[] args) {
		int status = 0;
		Ice.Communicator ic = null;
		try {
			//初始化通信器
			ic = Ice.Util.initialize(args);
			//传入远程服务单元的名称、网络协议、IP以及端口,获取Printer的远程代理,这里使用stringToProxy方式
			Ice.ObjectPrx base = ic.stringToProxy("SimplePrinter:default -p 10000");
			//通过checkedCast向下转型,获取Printer接口的远程,并同时检测根据传入的名称获取服务单元是否Printer的代理接口,如果不是则返回null对象
			PrinterPrx printer = PrinterPrxHelper.checkedCast(base);
			if(printer == null){
				throw new Error("Invalid proxy");
			}
			//把Hello world传给服务端,让服务端打印出来,因为这个方法最终会在服务端上执行
			printer.printString("Hello World!");
		} catch (Exception e) {
			e.printStackTrace();
			status = 1;
		}finally{
			if(ic != null){
				ic.destroy();
			}
		}
		System.exit(status);
	}
}

说明:和服务器一样
1)、调用Ice.initialize初始化Ice run time.
2)、获取远地打印机的代理,调用通信器的stringToProxy创建一个代理,所用参数是:"SimplePrinter:default -p 10000",注意,这个串包含了对象标示符和服务器所用的端口号一致
3)、stringToProxy返回的代理类型是Ice.ObjectPrx,这种类型位于接口和类的继承树的根部,但是实际与我们的打印机交谈,我们需要的嘶吼Printer接口、而不是Object接口的代理,为此,需要调用PrinterPrxHelper.checkCast进行向下转型,这个方法会发送一条消息给服务器,实际询问"这是Printer接口的代理吗?",如果是,这个调用就会返回Printer的一个代理,如果代理代表的是其他类型的接口,这个调用就会返回一个空代理。
4)、测试向下转型是否成功,如果不成功,抛出错误消息,终止客户。
5)、现在,在地址空间里面有了一个活的代理,可以调用printerString方法,把hello world串传给它,服务器会在它的终端上打印出这个串。
3.3.4、运行服务和客户端
在MyEclipse中,首先运行Server,启动服务器,再运行Client,在服务器的Console中我 们会看到打印出了"Hello World!".

四、总结
在本节主要介绍了一个简单的入门级(完整)的客户端和服务器的实例,从上可知,编写Ice应用涉及一下几个步骤:
1、编写Slice定义并编译它。
2、编写服务器并编译它。
3、编写客户端并编译它。


五、源码下载:
Ice简单应用源码下载