关于rmi的研究

来源:互联网 发布:java搞笑程序 编辑:程序博客网 时间:2024/04/29 12:23
关于rmi的研究
rmi有两个主要问题:
1. 调用如何从客户端传输到服务器端
 
 这个问题的是通过stub来解决的,stub负责和服务器通信,将调用传输到服务器并接收
   返回值
2. 由于stub类可以通过工具生成,但初始化必须在服务器端完成,所有如何将一个可用的
   stub传输到客户端就是我们最关心的问题

注:在1.4中stub类是必须的,在5.0中使用UnicastRemoteObject类可以不需要stub类
而由动态生成的Proxy类(实现远程接口,InvocationHandler是
RemoteObjectInvocationHandler)代替,具体见5.0的文档。如果不使用
UnicastRemoteObject类,则stub类在服务器端是必须的

rmi的核心问题是如何将一个可用的stub传输到客户端,大致有两种方法:
1. 最直观的方法,服务器和客户端直接建立连接,用任何可用的协议传输stub,缺点显而
   易见,客户端必须明确的知道服务器的地址及相关信息
2. 使用注册中心,如:rmiregistry和JNDI。这里主要讨论使用rmiregistry的情况

有一点需要注意,这里传输的只是stub的实例,而不是stub类的定义。所有stub类文件必须
在客户端和服务器端的classpath中,否则会抛出ClassNotFoundException。这样要求客户
端确实有些过分,我们希望客户端只要远程接口的定义就够了,这实际上很容易办到,下面
我们就来讨论这个问题

Dynamic code downloading usingRMI
(http://java.sun.com/j2se/1.4.2/docs/guide/rmi/codebase.html)

要下载类必须设置RMISecurityManager,默认是禁止下载的

客户端可以通过网络自动下载stub,此时stub不需要在客户端的classpath中,只需要远程
接口即可。

由rmiregistry作为注册中心的远程调用

在服务器绑定远程对象到rmiregistry时,rmiregistry必须能够找到该对象的stub,它在三
个地方寻找:
1. 启动它时的CLASSPATH环境变量

2. 启动它时所在的文件夹作为classpath寻找

3. 该stub中的codebase,这个值由服务器通过系统属性 java.rmi.server.codebase 设置

若找不到则无法绑定

客户端通过rmiregistry lookup到stub对象,虚拟机从两个位置寻找stub类的定义:
1. classpath中寻找

2. stub中的codebase,这个值由rmi服务器通过系统属性 java.rmi.server.codebase设置

由以上分析可见,服务器端只要正确设置了codebase,无论何种情况,远程调用都能顺利完成

不仅客户端可以从服务器下载类,服务器也可以从客户端下载类,只需要在客户端设置
codebase,安装RMISecurityManager即可

若服务器允许下载类,则客户端有可能在服务器上运行任何他希望的代码,如以下代码:
// 远程接口定义package rmi.server;import java.rmi.Remote;import java.rmi.RemoteException;public interface Service extends Remote {    public void hello() throws RemoteException;        public void runThread(Runnable thread) throws RemoteException;}

// 远程接口实现,main方法作为服务器package rmi.server;import java.rmi.Naming;import java.rmi.RMISecurityManager;import java.rmi.RemoteException;import java.rmi.server.RemoteStub;import java.rmi.server.UnicastRemoteObject;import java.security.Permission;public class ServiceImpl implements Service {    private RemoteStub stub;        private int count;    public ServiceImpl() throws RemoteException {        stub = UnicastRemoteObject.exportObject(this);        count = 0;    }    public void hello() throws RemoteException {        System.out.println("Hello World: count = " + (++count));    }    public void runThread(Runnable thread) throws RemoteException {        Thread t = new Thread(thread);        t.start();    }    public RemoteStub getStub() {        return stub;    }    public static void main(String[] args) {        System.setProperty("java.rmi.server.ignoreStubClasses", "true");        System.setProperty("java.rmi.server.codebase",                "file:///E:\\\\bahamut\\\\JavaWorkspace\\\\rmiserver\\\\classes\\\\");        if (System.getSecurityManager() == null) {            System.setSecurityManager(new RMISecurityManager() {                public void checkPermission(Permission p) {                }            });        }        try {            ServiceImpl service = new ServiceImpl();            Naming.rebind("service", service);            System.out.println("Server ready");        } catch (Exception e) {            e.printStackTrace();        }    }}

// 客户端package rmi.client;import java.rmi.Naming;import java.rmi.RMISecurityManager;import java.security.Permission;import rmi.server.Service;public class ServiceClient {        public static void main(String[] args) {        System.setProperty("java.rmi.server.codebase",        "file:///E:\\\\bahamut\\\\JavaWorkspace\\\\rmiclient\\\\classes\\\\");        if (System.getSecurityManager() == null) {            System.setSecurityManager(new RMISecurityManager() {                public void checkPermission(Permission p) {                }            });        }        try {            Service service = (Service) Naming.lookup("service");            service.hello();            Runnable runner = new Runner();            service.runThread(runner);        } catch (Exception e) {            e.printStackTrace();        }    }}
package rmi.client;

importjava.io.Serializable;

publicclassRunner implementsRunnable, Serializable {

publicvoidrun() {
System.out.println(
"i'mrunner");
}

}

服务器端生成stub并放在classpath中,codebase设置为能找到stub的路径,运行rmiregistry,
运行服务器。

客户端只需要Service类在classpath中,设置codebase为能找到Runner的路径,运行客户端。
客户端会从服务器下载stub,服务器会从客户端下载Runner。Runner可以执行任何操作甚至破坏
服务器上的数据。

多次运行客户端可以发现count在增长,也就是说对于export出的远程对象,状态是始终保存的