文章目录
  1. 1. 分布式对象和RMI
  2. 2. RMI程序构成
  3. 3. 应用实例详解
    1. 3.1. 系统模型
    2. 3.2. 具体实现
      1. 3.2.1. 远程对象接口(RMethod.java)
      2. 3.2.2. 远程对象实现(RMethodImpl.java)
      3. 3.2.3. 公共配置接口(Config.java)
      4. 3.2.4. RMI服务器端(Server.java)
      5. 3.2.5. RMI客户端(Client.java)
      6. 3.2.6. 安全策略文件(java.policy)
  4. 4. 编译已经运行方式
    1. 4.1. 命令行模式
      1. 4.1.1. 编译:
      2. 4.1.2. 运行服务器端:
      3. 4.1.3. 运行客户端:
      4. 4.1.4. 服务器端对应的输出为:
    2. 4.2. Eclipse中运行RMI程序
      1. 4.2.1. 下载RMI Eclipse插件:
      2. 4.2.2. 安装插件
      3. 4.2.3. 使用方法

这篇文档,写于2007年。当时的我还是研究生二年级的学生,恰巧有一门课叫做《分布式编程》,又恰巧当时做一个和分布式开发相关的项目。作为一份入门的文档,当时用Latex整理了一下,然后在开发小组内部传阅。

这次,作为第一篇技术文档,发表在这个站点上。作为纪念,也作为对当时的那股认真劲儿的祭奠,现在,越来越少了……

分布式对象和RMI

分布式对象技术主要是在分布式异构环境下简历应用系统框架和对象构件。在应用系统框架的支撑下,开发者可以将软件功能封装为更易于管理和使用的对象,这些对象可以跨越不同的软、硬件平台进行互操作。目前,分布式互操作标准主要有Microsoft的COM/DCOM标准、Oracle(前Sun)公司的Java RMI标准、OMG组织的CORBRA标准。

在Java中RMI在包java.rmi和java.rmi.server中。RMI是开发网络应用程序的一种强有力的技术,而不用担心底层的网络细节。RMI凭借一个更为通用的原创对象模型胜过了C/S模型。在这个模型里,有服务器定义客户端可以远程使用的对象,就好像它确实是一个运行在和客户端相同的环境下的本地对象。RMI隐藏了底层的有关传输方法参数和通过网络返回方法返回值的机制。一个参数或者返回值可以是一个简单值或者任何Serializable(可序列化:即对象可以转化成文件,存储在磁盘上)对象。

RMI程序构成

远程方法调用RMI是jdk 1.1 中引入的分布式对象软件包,他的出现大的简化了分布异构环境中Java应用程序直接的通信。

要开发一个基于RMI的应用程序,必须创建四个主要的类:

  • 远程对象接口(例: RMethod)

    这个接口中包含了所有远程方法的声明。在实际使用中,客户端只知道这个接口的存在,并不关心这个接口是如何实现的。
    它扩展了java.rmi.Remote接口,定义了远程对象执行的输出方法。该接口的每个方法都必须定义为抛出一个java.rmi.RemoteException异常,它是许多更专门的RMI异常类的父类。每个远程方法都必须声明为可以RemoteException移除,因为在通过网络调用远程方法的过程中,有很多意想不到的问题可能会引发错误。

  • 远程对象实现(例: RMethodImpl)

    这是远程对象接口(RMethod)的具体实现。它包含了实现远程接口的具体代码,可以在这些远程方法的实现代码中加入一些输出语句(或者log4j),以测试其被调用的情况。

    这个类同时是java.rmi.server.UnicastRemoteObject的子类。它代表了远程对象或者服务器对象。与声明远程方法来抛出RemoteException对象不同,该远程对象不需要做任何专门的事情来允许它的方法被远程调用。UnicastRemoteObject和RMI其余的基本结构将自动进行处理。

  • RMI服务器端(例:Server)

    RMI服务器端生成远程对象的一个实例,并用一个专用的URL进行注册。

    在注册时,首先要提供远程对象所在服务器的地址,同时需要对远程对象进行标识,也就是给远程对象一个名字。这个对象在服务器端和客户端必须一致,这样客户端才能找到服务器端的远程对象。

    服务器地址(serverAddress)+ 远程对象名字(objectName)
    

    构成了RMI的注册URL。其具体形式为:

    rmi://serverAddress:port/objectName
    

    端口号可以自己定义,默认端口是1099。如果使用默认端口,在URL里面可以不用给出。下面是一个URL的例子:

    rmi://192.168.1.123:2222/RemoteMethod
    
  • RMI客户端(例:Client)

    RMI客户端在远程RMI服务器端上查找服务对象,并将它转换成本地接口类型,然后像对待一个本地对象一样使用它。

应用实例详解

下面是一个简单的RMI实例,RMI客户端通过RMI服务器端提供的方法输出一个语句。例子虽然简单,但是涵盖了Java RMI调用的基本原理的方法。在实现复杂应用时,我们需要做的也只是完善远程对象的实现类而已。

系统模型

RMI系统模型图

具体实现

远程对象接口(RMethod.java)

这里作为实现,只定义了一个远程方法,在实际应用中,我们可以定义很多的远程方法,一起完成我们需要的功能。

1
2
3
4
5
import java.rmi.*;
public interface RMethod extends Remote {
String sayHello(String name) throws RemoteException;
}

远程对象实现(RMethodImpl.java)

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class RMethodImpl extends UnicastRemoteObject implements RMethod {
public RMethodImpl() throws RemoteException {
super();
}
public synchronized String sayHello(String name) throws RemoteException {
System.out.println("Client-" + name + ": invoking \" sayHello \"");
return "Hello " + name + "\n this is a message from Remote Method";
}
}

公共配置接口(Config.java)

在这个接口只能怪,定义了服务器和客户端共同使用的配置信息。这样,让某些信息变化时,只需要修改该接口,而不用去修改其他代码。

1
2
3
4
5
public interface Config {
String OBJECT_NAME = "RemoteMethod";
String SERVER_IP = "127.0.0.1";
int PORT = 1234;
}

RMI服务器端(Server.java)

服务器端的主要任务是创建远程对象实现的实例,并按照公用配置文件制定的对象名将这个实例注册到服务器端上。

当注册成功之后,服务器端程序就处于阻塞状态,等待客户端的远程对象访问请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.rmi.*;
import java.rmi.registry.*;
public class Server {
public static void main(String[] args){
new Server();
}
public Server(){
if (null == System.getSecurityManager()) {
System.setSecurityManager(new RMISecurityManager());
}
try {
try {
LocateRegistry.createRegistry(Config.PORT);
} catch (java.rmi.server.ExportException ex) {
System.out.println("Register the port failed:\n" + ex.getMessage());
}
RMethod rm = new RMethodImpl();
String objAddr = "rmi://" + Config.SERVER_IP
+ ":" + Config.PORT
+ "/" + Config.OBJECT_NAME;
java.rmi.Naming.rebind(objAddr, rm);
System.out.println("Server is running...");
} catch (Exception e) {
System.out.println("Server startup failed!");
e.printStackTrace();
}
}
}

RMI客户端(Client.java)

客户端通过调用服务器端提供的远程方法,来实现与服务器的通信。这样就不需要考虑具体的通信细节。只要像使用本地方法一样调用服务器端的远程方法,完成需要的功能。

要调用远程方法,客户端必须先构造一个RMI URL来找到远程对象(注意在服务器端我们只使用远程对象接口),然后通过远程对象来调用远程方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Client {
private String name;
private String hostURL;
private String obj;
public Client(String name){
this.name = name;
hostURL = "rmi://" + Config.SERVER_IP + ":" + Config.PORT + "/";
this.obj = Config.OBJECT_NAME;
}
public void callRMethod(){
try{
RMethod rm = (RMethod) java.rmi.Naming.lookup(hostURL + obj);
String result = rm.sayHello(name);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Client c1 = new Client("Monica");
c1.callRMethod();
Client c2 = new Client("Joy");
c2.callRMethod();
Client c3 = new Client("Ross");
c3.callRMethod();
Client c4 = new Client("Chandler");
c4.callRMethod();
}
}

安全策略文件(java.policy)

由于Java内置的安全策略对远程方法调用有一定的限制,所以我们必须编写一个安全策略文件(这是一个文本文件,文件名可以随便起)。在启动服务器端时把策略文件加载到虚拟机中,这样服务器端侧才能正常运行。

策略文件内容如下:

1
2
3
grant {
permission java.security.AllPermission;
};

编译已经运行方式

假设所有的文件都在同一个目录下。

命令行模式

编译:

$ javac *.java

运行服务器端:

$ rmic RMethodImpl
$ java -Djava.security.policy=java.policy Server
 Server is running...

运行客户端:

$ java Client
Hello Monica
  this is a message from Remote Method
Hello Joy
   this is a message from Remote Method
Hello Ross
   this is a message from Remote Method
Hello Chandler
  this is a message from Remote Method

服务器端对应的输出为:

$ java -Djava.security.policy=java.policy Server
Server is running...
Client-Monica: invoking " sayHello "
Client-Joy: invoking " sayHello "
Client-Ross: invoking " sayHello "
Client-Chandler: invoking " sayHello "

Eclipse中运行RMI程序

下载RMI Eclipse插件:

http://rmi-eclipse.sourceforge.net

安装插件

将下载后的文件: net.shotwave.eclipse.rmic_x.x.x.jar 复制到Eclipse的安装目录下plugin子目录下:

$ mv net.shotwave.eclipse.rmic_x.x.x.jar /usr/lib/eclipse/plugins/

重启 Eclipse之后,会发现Eclipse的菜单里多了一项:RMI。

使用方法

按照正常的步骤建立工程。

进行RMI的设置:

[RMI]---> [Add RMI Builder] ---> 选择当前工程

运行服务器端:

选择Server.java
    ---> 右键,[Run As]
    ---> [Run]
    ---> [Arguments] ---> [VM arguments]
    ---> 输入: -Djava.security.policy=java.policy
    ---> [Run]

这时在Eclipse底部的终端中就会看到服务器端程序已经运行起来了。

运行客户端:

选择Client.java
    ---> 右键,[Run As]
    ---> [Run]
    ---> [Java Application]
    ---> [Run]

这时在Eclipse底部的终端中就会看到客户端程序也已经运行起来了。

以上就是在Eclipse下运行RMI程序的方法。

文章目录
  1. 1. 分布式对象和RMI
  2. 2. RMI程序构成
  3. 3. 应用实例详解
    1. 3.1. 系统模型
    2. 3.2. 具体实现
      1. 3.2.1. 远程对象接口(RMethod.java)
      2. 3.2.2. 远程对象实现(RMethodImpl.java)
      3. 3.2.3. 公共配置接口(Config.java)
      4. 3.2.4. RMI服务器端(Server.java)
      5. 3.2.5. RMI客户端(Client.java)
      6. 3.2.6. 安全策略文件(java.policy)
  4. 4. 编译已经运行方式
    1. 4.1. 命令行模式
      1. 4.1.1. 编译:
      2. 4.1.2. 运行服务器端:
      3. 4.1.3. 运行客户端:
      4. 4.1.4. 服务器端对应的输出为:
    2. 4.2. Eclipse中运行RMI程序
      1. 4.2.1. 下载RMI Eclipse插件:
      2. 4.2.2. 安装插件
      3. 4.2.3. 使用方法