RMI原理及开发实例

http://blog.sina.com.cn/s/blog_492dbb6b0100faot.html

一、RMI原理简介

1、RMI定义和功能

RMI是Remote Method Invocation的简称,是J2SE的一部分,能够让程序员开发出基于Java的分布式应用。一个RMI对象是一个远 程Java对象,可以从另一个Java虚拟机上(甚至跨过网络)调用它的方法,可以像调用本地Java对象的方法一样调用远程对象的方法,使分布在不同的JVM中的对象的外表和行为都 像本地对象一样。

2、Stub和Skeleton介绍

在学习RMI的时,我们不能不讨论stub和skeleton作用和相关问题。他们是我们理解RMI原理的关键。我做个比方说明这两个概念。假如你是A,你想借D的工具,但是又不认识D的管家C,所以你找来B来帮你,B认识C。B在这时就是一个代理,代理你的请求,依靠自己的话语去借。C呢他负责D家东西收回和借出 ,但是要有D的批准。在得到D的批准以后,C再把东西给B,B呢再转给A。stub和skeleton在RMI中就是角色就是B和C,他们都是代理角色,在现实开发中隐藏系统和网络的的差异,这一部分的功能在RMI开发中对程序员是透明的。Stub为客户端编码远程命令并把他们发送到服务器。而Skeleton则是把远程命令解码,调用服务端的远程对象的方法,把结果在编码发给stub,然后stub再解码返回调用结果给客户端。

从JDK5.0以后,这两个类就不需要rmic来产生了,而是有JVM自动处理,实际上他们还是存在的。Stub存在于客户端,作为客户端的代理,让我们总是认为客户端产生了stub,接口没有作用。实际上stub类是通过Java动态类下载 机制下载的(具体内容请参考:Java RMI实现代码动态下载),它是由服 务端产生,然后根据需要动态的加载到客户端,如果下次再运行这个客户端该存根类存在于classpath中,它就不需要再下载了,而是直接加载。(具体的内部细节,需要参考Sun 的Rmi - Java Remote Method Invocation – Specification)。总的来说,stub是在服务端产生的,如果服务端的stub内容改变,那么客户端的也是需要同步更新。

3、Rmiregistry介绍

Rmiregistry需要在提供远程对象服务端启动,它提供了一个环境,说白了就是在内存中,开辟了一片空间,用来接受服务端服务程序的注册,产生一个类似于数据库,提供存储检索远程对象功能的注册表。这个RMI注册表,就是我们常听到的RMI名字服务。远程客户端,就是依靠它获得存根,调用远程方法。说到 这里有一个端口的问题,如果你在启动rmiregistry时,设定了非默认端口,那么需要在服务端和客户端统一使用该端口,否则就会有RemoteException的异常抛出,rmiregistry提 供的服务是针对特定的端口号的,不然在同一台机器上也是无法提供服务。

二、RMI实例开发

    在本例中我们实现一个求加法的远程服务,下面我们通过具体的例子,建立一个简单的远程计算服务和使用它的客户程序:  
一个正常工作的RMI系统由下面几个部分组成:
 ● 远程服务的接口定义
 ● 远程服务接口的具体实现
 ● 桩(Stub)和框架(Skeleton)文件
 ● 一个运行远程服务的服务器
 ● 一个RMI命名服务,它答应客户端去发现这个远程服务
 ● 类文件的提供者(一个HTTP或者FTP服务器)
 ● 一个需要这个远程服务的客户端程序
 下面我们一步一步建立一个简单的RMI系统。首先在你的机器里建立一个新的文件夹,以便放置我们创建的文件,为了简单起见,我们只使用一个文件夹存放客户端和服务端代码,并且在同一个目录下运行服务端和客户端。
  假如所有的RMI文件都已经设计好了,那么你需要下面的几个步骤去生成你的系统:
1、编写并且编译接口的Java代码
package lib.idc;

public interface Calculator extends Remote

{

    public long add(long a, long b) 

        throws java.rmi.RemoteException;

}

用命令编译Calculator.java

>>javac lib/idc/Calculator.java

2、编写并且编译接口实现的Java代码

package lib.idc;
import java.rmi.server.UnicastRemoteObject;

public class CalculatorImpl extends UnicastRemoteObject implements Calculator{ 

//这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常

    public CalculatorImpl() 

        throws java.rmi.RemoteException { 

        super(); 

    } 

 

    public long add(long a, long b) throws java.rmi.RemoteException { 

        return a + b; 

}

同样按照以上步骤编译CalculatorImpl.java

>>javac lib/idc/ CalculatorImpl.java

3、从接口实现类中生成桩(Stub)和框架(Skeleton)类文件

>>rmic lib.idc.CalculatorImpl
 在你的目录下运行上面的命令,成功执行完上面的命令你可以发现一个Calculator_stub.class文件,假如你是使用的Java2SDK,那么你还可以发现Calculator_Skel.class文件。(我的没有)

4、编写远程服务的主运行程序
 远程RMI服务必须是在一个服务器中运行的。CalculatorServer类是一个非常简单的服务器。
package lib.idc;
import java.rmi.Naming;
public class CalculatorServer {
public CalculatorServer() {
try{
    Calculator c = new CalculatorImpl();
    Naming.rebind("rmi://localhost:1099/CalculatorService", c);
  }catch (Exception e) {
       System.out.println("Trouble: " + e);
  }
 }
  public static void main(String args[]) {
  new CalculatorServer();
   }
}
建立这个服务器程序,然后保存到你的目录下。

5、编写RMI的客户端程序

package lib.idc;

import java.rmi.Naming; 

import java.rmi.RemoteException; 

import java.net.MalformedURLException; 

import java.rmi.NotBoundException; 

import java.rmi.RMISecurityManager;

public class CalculatorClient { 

  public static void main(String[] args) {

        if(System.getSecurityManager()==null)

              System.setSecurityManager(new RMISecurityManager());

  

        try { 

            Calculator c = (Calculator)

                           Naming.lookup(

                 "rmi://localhost/CalculatorService"); 

                System.out.println( c.add(4, 5) );

} catch (MalformedURLException murle) { 

              System.out.println("MalformedURLException"); 

            

               catch (RemoteException re) { 

              System.out.println("RemoteException"); 

          } 

}
6、运行RMI系统

    完成以上步骤后,运行启动服务注册命令:

>>rmiregistry(注意所有命令的运行都是在lib/idc的上级目录,即. /lib/idc)

此命令执行后,将启动注册服务程序,然后打开新的控制台窗口(如果在应用程序中启动服务端程序,也必须先生成桩和框架类文件和运行rmiregistry命令)。同样进入目录. /lib/idc,执行启动服务端程序。因为RMI的安全机制将在服务端发生作用,所以你必须增加一条安全策略: 
grant {
permission java.security.AllPermission "", "";
};并将其添加到\..\jre\lib\security\ java.policy文件之后,该文件位于指定的jre路径下。

>> javac lib/idc/CalculatorServer.java

>> java  lib.idc. CalculatorServer

这一步完成后打开第三个控制台窗口,进入目录. /lib/idc,执行

命令启动客户端程序

>> javac lib/idc/ CalculatorClient.java

>> java  lib.idc. CalculatorClient

至此完成RMI系统的运行。

三、常见错误

1、error unmarshalling return; nested exception is:

        java.lang.ClassNotFoundException: rmi.RmiInterfaceImp_Stub (no security manager: RMI class loader disabled)

解决方法:在客户端添加以上红色部分代码。

2、Exception in thread "main" java.security.AccessControlException: access denied (

java.net.SocketPermission 211.69.205.250:1099 connect,resolve)

解决方法:新建文件如policy.txt 内容为:

grant {
permission java.security.AllPermission "", "";
};并保存在客户端程序目录下,然后执行

>>java  -Djava.security.policy=policy.txt lib.idc.CalculatorClient

3、>>rmic RmiInterfaceImp

error: 未找到类 RmiInterfaceImp。1 个错误

解决方法:出现此问题一般是RmiInterfaceImp的路径问题,如在此处改为lib.idc.snatch.RmiInterfaceImp,表示RmiInterfaceImp所在的包。

4、error unmarshalling return; nested exception is:

        java.lang.ClassNotFoundException: lib.idc.snatch.RmiInterfaceImp_Stub

解决方法:出现此类问题的原因是找不到RmiInterfaceImp_Stub,但是在同样的目录下有时可以找到,有时又找不到了,真是活见鬼了!这个问题还没彻底解决,如果哪位兄弟解决了,还请不忘相告一声。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值