转载过来的java_rmi

13年前

RMI和corba是两种最重要和使用最广泛的分布式对象系统。 每种都有它的长处和短处。这两种系统都在从电子商务到卫生保健等不同的行业成功的使用。在项目中使用这两种分布机制中的任何一种都是一项很困难的任务。本文介绍了RMI和corba的机理和最主要的是显示了如何开发一个有用的程序(一个从远程站点下载文件的程序)。于是有以下内容:

  • 一个分布式系统的简介
  • 一个RMI和corba的简介
  • 让你体验开发一个RMI和corba程序的滋味。
  • 说明如何使用RMI和corba从远程机器交换文件
  • 提供一个RMI和Corba的比较。

客户/服务端模式

客户/服务模式是一个分布式计算应用。它通过使用一个应用程序(客户)和另一个程序(服务端)交换数据。在这样的一个例子里面客户端和服务端一般使用同样的语言来编写,使用相同的协议来相互通信。
在客户/服务模式应用到各种各样的地方的过程中,使用低层次的socket来开发是很典型的。使用socket来开发客户/服务端模式意味着我们必须自己设计一种协议,该协议包含客户端和服务端都统一的命令集 ,使得客户端和服务端能够通过这个协议来通信。例如:HTTP协议提供了一个get的方法。所有的web服务器软件都集成了该功能,而所有的浏览器软件都能够使用该功能来获得资料。

分布式对象模式

分布式对象系统是一个对象集合,通过定义很完善的统一的接口来分隔开的要求服务(客户端)和功能服务(服务端)。换句话说客户端和公共服务的提供分隔开,这些服务包括数据表现和执行的代码。这是一个分辨分布式对象模式和客户/服务模式的主要不同。

在分布式对象模式里,客户端发送一个消息到一个对象,由这个对象解释这个消息然后决定应该由什么服务来完成。这个服务,方法或选择的完成可能是被一个对象或是被一个broker。RMI和corba就是这种模式的例子。

RMI

RMI是一个分布式对象模式。它使得使用java开发分布式程序更加容易。由于不需要设计协议(这基本是一个错误的任务) 使得使用RMI开发分布式程序比使用socket更加容易。在RMI里面设计者就象在调用一个本地的类的方法一样,而实际上是在调用的时候相应的参数被发送到远端的对象和然后被解释。最后结果返回给调用者。

一个 RMI应用的流程

使用 RMI开发一个分布式应用包括如下几个步骤
1)定义一个远端的接口
2)实现这个远端的接口
3)开发一个服务端
4)开发一个客户端
5)生成Stubs 和Skeletons,运行RMI注册器,服务端 和客户端
我们现在通过开发一个文件交换程序来解释这些步骤

例子:文件交换程序

这个应用允许客户端从服务端交换(或下载)所有类型的文件。第一步是定义一个远程的接口,这个接口指定了的签名方法将被服务端提供和被客户端调用。

定义一个远程接口

这个程序的远程接口在代码例子1中列出,接口FileInterface提供一个downloadFile这个带一个字符窜(文件名)变量的的方法,然后返回以一个字符窜序列的形式的相应文件数据。

代码例子1: FileInterface.java

import java.rmi.Remote;  import java.rmi.RemoteException;    public interface FileInterface extends Remote {     public byte[] downloadFile(String fileName) throws     RemoteException;  }  

注意接口FileInterface的如下特征:

  • 它必须定义成public,这是为了让客户端能够通过调用远程接口来间接调用远程的对象。
  • 必须使用从Remote接口扩展过来,这是创建一个远程的对象的需要。
  • 每个接口方法中必须抛出java.rmi.RemoteException错误。

实现远程的接口

下一步是实现远程的接口FileInterface。实现的例子在代码例子 2中列出。类FileImpl从UnicastRemoteObject扩展来。这显示出FileImpl类是用来创建一个单独的,不能复制的,远程的对象,
这个对象使用RMI 的默认的基于TCP的通信方式。

代码例子 2: FileImpl.java

import java.io.*;  import java.rmi.*;  import java.rmi.server.UnicastRemoteObject;    public class FileImpl extends UnicastRemoteObject    implements FileInterface {       private String name;       public FileImpl(String s) throws RemoteException{        super();        name = s;     }       public byte[] downloadFile(String fileName){        try {           File file = new File(fileName);           byte buffer[] = new byte[(int)file.length()];           BufferedInputStream input = new        BufferedInputStream(new FileInputStream(fileName));           input.read(buffer,0,buffer.length);           input.close();           return(buffer);        } catch(Exception e){           System.out.println("FileImpl: "+e.getMessage());           e.printStackTrace();           return(null);        }     }  }  

编写服务端
第3步是实现一个服务端。有3件事服务端需要去做:
1)创建一个RMISecurityManager实例,然后安装它。
2)创建一个远程对象的实例(这个例子中是FileImpl )
3)使用RMI注册工具来注册这个对象。
代码例子 3中显示了如何操作的。
代码例子 3: FileServer.java

import java.io.*;  import java.rmi.*;    public class FileServer {     public static void main(String argv[]) {        if(System.getSecurityManager() == null) {           System.setSecurityManager(new RMISecurityManager());        }        try {           FileInterface fi = new FileImpl("FileServer");           Naming.rebind("//127.0.0.1/FileServer", fi);        } catch(Exception e) {           System.out.println("FileServer: "+e.getMessage());           e.printStackTrace();        }     }  }  

声明Naming.rebind("//127.0.0.1/FileServer", fi) 中假定了RMI注册工具(RMI registry )使用1099端口并在运行中。如果你在其他的端口运行了RMI注册工具,你必须在这个声明中定义。例如如果RMI注册工具在4500端口运行。你的声明要变成
Naming.rebind("//127.0.0.1:4500/FileServer", fi)
另外我们已经同时假定了我们的服务端和RMI注册工具是运行在同一台机器上的。如果不是的话你要修改rebind方法中的地址。

编写客户端 下一步是编写一个客户端,客户端可以远程调用远程接口(FileInterface)中说明的任何一个方法。无论如何实现,客户端必须先从RMI注册工具获得一个远程对象的引用。当引用获得后方法downloadFile 被调用。客户端的例子在代码例子4中,执行过程中客户端从命令行中获得两个参数,第一个是要下载的文件名,第二个是要下载的机器的地址。对应地址的机器上运行服务端。

代码例子 4: FileClient.java

import java.io.*;   import java.rmi.*;    public class FileClient{     public static void main(String argv[]) {        if(argv.length != 2) {          System.out.println("Usage: java FileClient fileName machineName");          System.exit(0);        }        try {           String name = "//" + argv[1] + "/FileServer";           FileInterface fi = (FileInterface) Naming.lookup(name);           byte[] filedata = fi.downloadFile(argv[0]);           File file = new File(argv[0]);           BufferedOutputStream output = new             BufferedOutputStream(new FileOutputStream(file.getName()));           output.write(filedata,0,filedata.length);           output.flush();           output.close();        } catch(Exception e) {           System.err.println("FileServer exception: "+ e.getMessage());           e.printStackTrace();        }     }  }  

运行程序
为了运行程序我们必须生成stubs 和 skeletons,为了生成stubs 和 skeletons,我们使用rmic来编译:
prompt> rmic FileImpl
将会生成两个文件FileImpl_Stub.class和 FileImpl_Skel.class. stub是客户端的代理而skeleton是服务端的框架。
下一步是编译服务端和客户端。使用javac来编译。注意如果服务端和客户端在两个不同的机器,为了编译客户端你必须复制一个FileInterface接口。
最后,到你运行RMI注册工具和运行服务端和客户端的时候了。使用rmiregistry 或者 start rmiregistry 命令来运行RMI注册工具到window系统的默认的端口上,要运行RMI注册工具在一个其他的端口的话使用端口参数。
prompt> rmiregistry portNumber
RMI注册工具运行之后,你要运行服务FileServer,因为RMI的安全机制将在服务端发生作用,所以你必须增加一条安全策略。以下是对应安全策略的例子
grant {
permission java.security.AllPermission "", "";
};
注意:这是一条最简单的安全策略,它允许任何人做任何事,对于你的更加关键性的应用,你必须指定更加详细安全策略。
现在为了运行服务端,你需要除客户类(FileClient.class)之外的所有的类文件。确认安全策略在policy.txt文件之后,使用如下命令来运行服务器。
prompt> java -Djava.security.policy=policy.txt FileServer
为了在其他的机器运行客户端程序你需要一个远程接口(FileInterface.class) 和一个stub(FileImpl_Stub.class)。 使用如下命令运行客户端
prompt> java FileClient fileName machineName
这里fileName是要下载的文件名,machineName 是要下载的文件所在的机器(也是服务端所在的机器)
如果所有都可以了话,当客户端运行后,这个文件将下载到本地。