??xml version="1.0" encoding="utf-8" standalone="yes"?>
一、RMIQ远E方法调用)的组?/font>
一个正常工作的RMIpȝ׃面几个部分组成:
1、远E服务的接口定义
2、远E服务接口的具体实现
3、桩QStubQ和框架QSkeletonQ文?
4、一个运行远E服务的服务?
5、一个RMI命名服务Q它允许客户端去发现q个q程服务
6、类文g的提供者(一个HTTP或者FTP服务器)
7、一个需要这个远E服务的客户端程?
二、RMIQ远E方法调用)的工作原?
RMIpȝl构Q在客户端和服务器端都有几层l构?
--------- ----------
| 客户 | | 服务器|
---------- ----------
| |
------------- ----------
| 占位E序 | | 骨干|?|
-------------- -----------
| |
------------------------------------
| q?E????|
------------------------------------
| |
------------------------------------
| ???|
------------------------------------
Ҏ调用从客户对象经占位E序QStub)、远E引用层(Remote Reference Layer)和传输层QTransport LayerQ向下,传递给LQ然后再ơ经?输层Q向上穿q远E调用层和骨q网QSkeletonQ,到达服务器对象?占位E序扮演着q程服务器对象的代理的角Ԍ使该对象可被客户ȀzR?q程引用层处理语义、管理单一或多重对象的通信Q决定调用是应发往一个服务器q是多个。传输层理实际的连接,q且q追t可以接受方法调用的q程对象。服务器端的骨干|完成对服务器对象实际的Ҏ调用Qƈ获取q回倹{返回值向下经q程引用层、服务器端的传输层传递回客户端,再向上经传输层和q程调用层返回。最后,占位E序获得q回倹{?
要完成以上步骤需要有以下几个步骤Q?
1、生成一个远E接?
2、实现远E对?服务器端E序)
3、编写服务器E序 、注册远E对象、启动远E对?br />
4、编写客L?
在JDK1.5之后Q用java提供的API会更加的简单,可以参照下面的例子;
三、例?/p>
1、远E接?/p>
接口名:com.liuxiang.rmi.download.IRMI
该接口定义了一个方法,用于提供q程服务Q?/p>
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* q程对象接口
* @author liuxiang
* 2007-8-30 下午09:39:15
*
*/
public interface IRMI extends Remote{
public Object invoke(ITask task) throws RemoteException;
}
2、实现远E对?服务器端E序)
cdQcom.liuxiang.rmi.download.IRMIImpl
实现了远E接口定义的ҎQ该实现q程对象中,调用了传入参数的task.doWork()ҎQ同时执行一个本地调用,调用一个播放Mp3的代码段ProcessCaller.callMp3();
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import com.liuxiang.callwindow.ProcessCaller;
/**
* q程对象的实?br />
* @author liuxiang
* 2007-8-30 下午09:41:32
*
*/
public class IRMIImpl extends UnicastRemoteObject implements IRMI {
protected IRMIImpl() throws RemoteException {
super();
}
/**
*
*/
private static final long serialVersionUID = 6131922116577454476L;
/* (non-Javadoc)
* @see com.liuxiang.rmi.download.IRMI#invoke(com.liuxiang.rmi.download.ITask)
*/
public Object invoke(ITask task) throws RemoteException {
System.out.println("注意Q这是一个远E调?);
Object obj = task.doWork();
System.out.println("调用ITask.doWork()Ҏ的返回|"+obj.toString());
//客户端调用,可以在服务器端播N要的音乐
ProcessCaller.callMp3();
return obj;
}
}
3、播放Mp3代码D늚源代码如下:
/**
*
*/
package com.liuxiang.callwindow;
/**
* 在java中调用windowsE序
* @author liuxiang 2007-8-30 下午10:26:20
*
*/
public class ProcessCaller {
/**
* 调用WindowsE序
* 利用Windows Media Player播放mp3音乐
*/
public static void callMp3() {
Runtime ru = Runtime.getRuntime();
try {
// 调用播放器文件播放指定MP3
Process p1 = ru
.exec("C:\Program Files\Windows Media Player\wmplayer F:/music/lx/刘若?后来.mp3");
} catch (Exception e) {
}
}
/**
* @param args
*/
public static void main(String[] args) {
callMp3();
}
}
4、Q务接?/p>
该接口定义了q程Ҏ需要传递的参数
/**
*
*/
package com.liuxiang.rmi.download;
import java.io.Serializable;
/**
* d接口
* @author liuxiang
* 2007-8-30 下午09:35:53
*
*/
public interface ITask extends Serializable{
public Object doWork();
}
5、Q务接口实?/p>
该实现定义了q程Ҏ需要传递的参数
package com.liuxiang.rmi.download;
/**
* d实现c?br />
* @author liuxiang
* 2007-8-31 上午09:08:57
*
*/
public class TaskImpl implements ITask{
public Object doWork() {
System.out.println("当前E序处于q程调用?);
return "动态上载对象的q回?;
}
}
6、编写服务器E序 、注册远E对象、启动远E对?/p>
代码Q远E对象的注册c?该类应该在服务器端执?执行之后Q?该机器将变ؓRMI服务?客户端可以通过正确的url来访问;服务器上的远E对象;执行对外报露的方?/p>
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
/**
* q程对象的注册类 该类应该在服务器端执?执行之后Q?br />
* 该机器将变ؓRMI服务?客户端可以通过正确的url来访?br />
* 服务器上的远E对象;执行对外报露的方?br />
*
* @author liuxiang 2007-8-30 下午09:44:54
*
*/
public class RMIServer {
/**
* 如果没有创徏一个RegistryQNaming是不会帮助你创徏的?q是自己手工创徏的比较的?br />
*
* 量用下面的自己装的bindҎ来注册远E对?br />
*
* @throws Exception
*/
public static void registRemoteObject() throws Exception {
IRMIImpl impl = new IRMIImpl();
Naming.rebind("rmi://219.233.8.97:1111/mytask", impl);
System.out.println("bound success!");
}
/**
* 创徏一个Registry对象
*
* @return q回一个Registry对象
*/
private static Registry createRegistry() {
Registry registry = null;
int port = 1111;
try {
registry = LocateRegistry.getRegistry(port);
registry.list();
System.out.println("Register the exist server!");
} catch (final Exception e) {
try {
registry = LocateRegistry.createRegistry(port);
System.out.println("Register the exist server!port=" + port);
} catch (final Exception ee) {
ee.printStackTrace();
}
}
return registry;
}
/**
* 对象注册到rmi服务器上
*/
public static void bind() {
Registry registry = null;
registry = createRegistry();
try {
IRMIImpl impl = new IRMIImpl();
registry.rebind("mytask", impl);
System.out.println("mytask server start!");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
try {
bind();
} catch (Exception e) {
e.printStackTrace();
}
}
}
q行该服务端代码后,会注册一个远E服务对象,通过恰当的URL可以讉Kq程对象中的ҎQ运行后的结果如下:
Register the exist server!port=1111
mytask server start!
7、编写客L?/font>
代码首先获取一个远E对象,然后如同本地调用一P调用q程对象中的Ҏ?/font>
/**
*
*/
package com.liuxiang.rmi.download;
import java.rmi.Naming;
/**
* @author liuxiang
* 2007-8-30 下午09:47:41
*
*/
public class RMIClient {
/**
* 调用q程对象中的Ҏ
* @throws Exception
*/
public static void getRemoteObject() throws Exception{
IRMI obj = (IRMI)Naming.lookup("rmi://localhost:1111/mytask"); //得到q程发布的服?br />
TaskImpl task = new TaskImpl();
Object result = obj.invoke(task); //调用q程服务的方?br />
System.out.println(result.toString());
}
/**
* @param args
*/
public static void main(String[] args) {
try {
getRemoteObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后,q行客户端代码,可以看到控制台分别输Z如下的内容:
Server端输Z更多的内容,如下Q?/font>
Register the exist server!port=1111
mytask server start!
注意Q这是一个远E调?br />
当前E序处于q程调用?br />
调用ITask.doWork()Ҏ的返回|动态上载对象的q回?
同时可以看到Q会调用了Windows的Mediaplay播放本地盘的Mp3歌曲Q?/font>
Client端输入了如下内容Q?/font>
动态上载对象的q回?br />
四、RMI介绍
RMIQRemote Method InvocationQ远E方法调用)是用Java在JDK1.1中实现的Q它大大增强了Java开发分布式应用的能力。Java作ؓ一U风靡一时的|络开发语aQ其巨大的威力就体现在它强大的开发分布式|络应用的能力上Q而RMI是开发百分之癄Java的网l分布式应用pȝ的核心解x案之一。其实它可以被看作是RPC的Java版本。但是传lRPCq不能很好地应用于分布式对象pȝ。而Java RMI 则支持存储于不同地址I间的程序对象之间彼此q行通信Q实现远E对象之间的无缝q程调用?nbsp;
RMI目前使用Javaq程消息交换协议JRMPQJava Remote Messaging ProtocolQ进行通信。JRMP是专为Java的远E对象制定的协议。因此,Java RMIhJava?Write Once,Run Anywhere"的优点,是分布式应用pȝ的百分之癄Java解决Ҏ。用Java RMI开发的应用pȝ可以部v在Q何支持JREQJava Run Environment JavaQ运行环境)的^C。但׃JRMP是专为Java对象制定的,因此QRMI对于用非Java语言开发的应用pȝ的支持不뀂不能与用非Java语言书写的对象进行通信?
RMI为采用Java对象的分布式计算提供了简单而直接的途径。这些对象可以是新的Java对象Q也可以是围l现有API的简单的Java包装E序。Java体现?#8220;~写一ơ就能在M地方q行的模式。而RMI可将Java模式q行扩展Q之可在Q何地方运?#8221;?
因ؓRMI是以Java为核心的Q所以,它将Java的安全性和可移植性等强大功能带给了分布式计算。您可将代理和梢?务逻辑{属性移动到|络中最合适的地方。如果您要扩展Java在系l中的用,RMI您充分利用其强大功能?
RMI可利用标准Java本机Ҏ接口JNI与现有的和原有的pȝ相连接。RMIq可利用标准JDBC包与现有的关pL据库q接。RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与目前用非Java语言的现有服务器q行通信Q而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展用时充分利用Java的强大功能?/font>
1、接?br />
W一步就是徏立和~译服务接口的Java代码。这个接口定义了所有的提供q程服务的功能,在这里我们所有完成的是加减乘除Q下面是源程序:
注意Q这个接口承自RemoteQ每一个定义的Ҏ都必LZ个RemoteException异常对象?br />
建立q个文gQ把它存攑֜刚才的目录下Qƈ且编译?br />
2、接口的具体实现
下一步,我们p写远E服务的具体实现Q这是一个CalculatorImplcLӞ
q个实现cM用了UnicastRemoteObject去联接RMIpȝ。在我们的例子中Q我们是直接的从UnicastRemoteObjectq个cMl承的,事实上ƈ不一定要q样做,如果一个类不是从UnicastRmeoteObject上承,那必M用它的exportObject()Ҏ去联接到RMI?br />
如果一个类l承自UnicastRemoteObjectQ那么它必须提供一个构造函数ƈ且声明抛Z个RemoteException对象。当q个构造函数调用了super()Q它久激zUnicastRemoteObject中的代码完成RMI的连接和q程对象的初始化?br />
3、Stubs 和Skeletons
下一步就是要使用RMI~译器rmic来生成桩和框架文Ӟq个~译q行在远E服务实现类文g上?br />
在IDE中build所有程?br />
>rmic rmiDemoCalculatorImpl
在你的目录下q行上面的命令,成功执行完上面的命o你可以发C个Calculator_stub.class文gQ如果你是用的Java2SDKQ那么你q可以发现Calculator_Skel.class文g?br />
4、主机服务器
q程RMI服务必须是在一个服务器中运行的。CalculatorServercL一个非常简单的服务器?br />
5、客L
客户端源代码如下Q?br />
保存q个客户端程序到你的目录下(注意q个目录是一开始徏立那个,所有的我们的文仉在那个目录下Q?br />
在IDE中build所有程序?br />
6、运行RMIpȝ
现在我们建立了所有运行这个简单RMIpȝ所需的文Ӟ现在我们l于可以q行q个RMIpȝ啦!来n受吧?br />
我们是在命o控制Cq行q个pȝ的,你必d启三个控制台H口Q一个运行服务器Q一个运行客LQ还有一个运行RMIRegistry?br />
首先q行注册E序RMIRegistryQ你必须在包含你刚写的类的那么目录下q行q个注册E序?br />
>rmiregistry
好,q个命o成功的话Q注册程序已l开始运行了Q不要管他,现在切换到另外一个控制台Q在W二个控制台里,我们q行服务器CalculatorServiceQ因为RMI的安全机制将在服务端发生作用,所以你必须增加一条安全策略。以下是对应安全{略的例?nbsp;
grant {
permission java.security.AllPermission "", "";
};
注意:q是一条最单的安全{略,它允怓Q何h做Q何事,对于你的更加关键性的应用,你必L定更加详l安全策略?br />
现在Zq行服务端,你需要除客户c?CalculatorClient.class)之外的所有的cL件。确认安全策略在policy.txt文g之后,使用如下命o来运行服务器?br />
> java -Djava.security.policy=C:\Documents and Settings\Administrator\RMI\build\classes\ rmiDemo.CalculatorServer
q个服务器就开始工作了Q把接口的实现加载到内存{待客户端的联接。好现在切换到第三个控制収ͼ启动我们的客L?br />
Z在其他的机器q行客户端程序你需要一个远E接?Calculator.class) 和一个stub(CalculatorImpl_Stub.class)?nbsp;使用如下命oq行客户?br />
> java -Djava.security.policy=C:\Documents and Settings\Administrator\RMI\build\classes\ rmiDemo. CalculatorClient
如果所有的q些都成功运行,你应该看C面的输出Q?br />
1
9
18
3