Java RMI 简单示例 1
定义一个远程接口:
import java.rmi.Remote;
public interface IHello extends Remote {
public String sayHello(String name) throws java.rmi.RemoteException;
}
实现远程的接口 - 服务端就在此远程接口的实现类中
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements IHello {
// 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常
protected HelloImpl() throws RemoteException {
super();
}
/**
* 说明清楚此属性的业务含义
*/
private static final long serialVersionUID = 4077329331699640331L;
public String sayHello(String name) throws RemoteException {
return "Hello " + name + " ^_^ ";
}
public static void main(String[] args) {
try {
IHello hello = new HelloImpl();
java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello);
System.out.print("Ready");
} catch (Exception e) {
e.printStackTrace();
}
}
}
新建RMI客户端调用程序
import java.rmi.Naming;
public class Hello_RMI_Client {
public static void main(String[] args) {
try {
IHello hello = (IHello) Naming.lookup("rmi://localhost:1099/hello");
System.out.println(hello.sayHello("zhangxianxin"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
编译并运行
- 用 javac 命令编译 IHello.java HelloImpl.java Hello_RMI_Client.java
>javac *.java
- 用 rmic 命令生成 桩stub 和 框架skel 文件
>rmic HelloImpl
成功执行完上面的命令可以发现生成一个HelloImpl_stub.class文件,如果JDK是使用Java2SDK,那么还可以发现多出一个HelloImpl_Skel.class文件。如果服务端程序与客户端程序在同一台机器上并在同一目录中,则可以省略掉接口实现类生成的桩和框架文件,但这就失去了使用RMI的意义,而如果要在不同的JVM上运行时,客户端程序就必须得依靠服务端运程方法实现的桩和框架文件以及接口类。
- 运行注册程序 RMIRegistry, 必须在包含刚写的类的目录下运行这个注册程序
>rmiregistry
注册程序开始运行了,不要管它,现在切换到另外一个控制台运行服务器。
- 运行服务器HelloImpl
>java HelloImpl
当启动成功出现 Ready... ... 这个服务器就开始工作了,把接口的实现加载到内存等待客户端的连接。现在切换到第三个控制台,启动我们的客户端。
- 启动客户端 - 为了在其他的机器上运行客户端程序需要一个远程接口 IHello.class 和一个stub(HelloImpl_Stub.class)。 使用如下命令运行客户端
>java Hello_RMI_Client
当运行成功会在客户端控制台打印:Hello zhangxinaxin ^_^
如果不想在控制台上开启RMI注册程序 RMIRegistry 的话,可以在RMI服务类程序中添加 LocateRegistry.createRegistry(1099):
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements IHello {
// 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常
protected HelloImpl() throws RemoteException {
super();
}
private static final long serialVersionUID = 4077329331699640331L;
public String sayHello(String name) throws RemoteException {
return "Hello " + name + " ^_^ ";
}
public static void main(String[] args) {
try {
IHello hello = new HelloImpl();
LocateRegistry.createRegistry(1099); //加上此程序,就可以不要在控制台上开启RMI的注册程序,1099是RMI服务监视的默认端口
java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello);
System.out.print("Ready");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Java RMI 简单示例 2
以下用一个文件交换程序来介绍RMI的应用。这个应用允许客户端从服务端交换(或下载)所有类型的文件。第一步是定义一个远程的接口,这个接口指定的签名方法将被服务端提供和被客户端调用。
定义一个接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IFileUtil extends Remote {
public byte[] downloadFile(String fileName) throws RemoteException;
}
实现远程的接口
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class FileUtilImpl extends UnicastRemoteObject implements IFileUtil {
protected FileUtilImpl() throws RemoteException {
super();
}
private static final long serialVersionUID = 7594622080290821912L;
public byte[] downloadFile(String fileName) throws RemoteException{
File file = new File(fileName);
byte buffer[] = new byte[(int) file.length()];
int size = buffer.length;
System.out.println("download file size = "+size +"b");
if(size>1024*1024*10){//限制文件大小不能超过10M,文件太大可能导制内存溢出!
throw new RemoteException("Error:<The File is too big!>");
}
try {
BufferedInputStream input = new BufferedInputStream(new FileInputStream(fileName));
input.read(buffer, 0, buffer.length);
input.close();
System.out.println("Info:<downloadFile() hed execute successful!>");
return buffer;
} catch (Exception e) {
System.out.println("FileUtilImpl: " + e.getMessage());
e.printStackTrace();
return null;
}
}
}
编写服务器
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
public class FileUtilServer {
public static void main(String argv[]) {
try {
IFileUtil file = new FileUtilImpl();
//LocateRegistry.createRegistry(1099); //加上此程序,就可以不要在控制台上开启RMI的注册程序,1099是RMI服务监视的默认端口
Naming.rebind("rmi://127.0.0.1/FileUtilServer", file);
System.out.print("Ready");
} catch (Exception e) {
System.out.println("FileUtilServer: " + e.getMessage());
e.printStackTrace();
}
}
}
声明Naming.rebind("rmi://127.0.0.1/FileUtilServer", file) 中假定了RMI注册工具(RMI registry )使用并启动了1099端口。如果在其他端口运行了RMI注册工具,则必须在这个声明中定义。例如,如果RMI注册工具在4500端口运行,则声明应为: Naming.rebind("rmi://127.0.0.1:4500/FileUtilServer", file)
另外我们已经同时假定了我们的服务端和RMI注册工具是运行在同一台机器上的。否则需要修改rebind方法中的地址。
编写客户端
客户端可以远程调用远程接口(FileInterface)中的任何一个方法。无论如何实现,客户端必须先从RMI注册工具中获取一个远程对象的引用。当引用获得后,方法downloadFile被调用。在执行过程中,客户端从命令行中获得两个参数,第一个是要下载的文件名,第二个是要下载的机器的地址,在对应地址的机器上运行服务端。
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.rmi.Naming;
public class FileUtilClient {
public static void main(String args[]) {
if (args.length != 3) {
System.out.println("第一个参数:RMI服务的IP地址");
System.out.println("第二个参数:要下载的文件名");
System.out.println("第三个参数:要文件保存位置");
System.exit(0);
}
try {
String name = "rmi://" + args[0] + "/FileUtilServer";
IFileUtil fileUtil = (IFileUtil) Naming.lookup(name);
byte[] filedata = fileUtil.downloadFile(args[1]);
if(filedata==null){
System.out.println("Error:<filedata is null!>");
System.exit(0);
}
File file = new File(args[2]);
System.out.println("file.getAbsolutePath() = "+file.getAbsolutePath());
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath()));
output.write(filedata, 0, filedata.length);
output.flush();
output.close();
System.out.println("~~~~~End~~~~~");
} catch (Exception e) {
System.err.println("FileUtilServer exception: " + e.getMessage());
e.printStackTrace();
}
}
}
运行程序
- 为了运行程序,我们必须使用rmic来编译生成stubs和skeletons:
>rmic FileUtilImpl
这将会生成FileUtilImpl_Stub.class和FileUtilImpl_Skel.class两个文件。stub是客户端的代理,而skeleton是服务端的框架。服务端和客户端采用javac来编译(如果服务端和客户端在两个不同的机器,则必须复制一个IFileUtil接口)。
- 使用rmiregistry或者start rmiregistry 命令来运行RMI注册工具到window系统默认的端口上:
> rmiregistry portNumber
此处的portNumber为端口,RMI注册工具运行之后,需要运行服务FileUtilServer,因为RMI的安全机制将在服务端发生作用,所以必须增加一条安全策略: grant{permission java.security.AllPermission "", "";};
- 为了运行服务端,需要有除客户类(FileUtilClient.class)之外所有的类文件。确认安全策略在policy.txt文件之后,使用如下命令来运行服务器。
> java -Djava.security.policy=policy.txt FileUtilServer
- 为了在其他的机器运行客户端程序,需要一个远程接口(IFileUtil.class)和一个stub(FileUtilImpl_Stub.class)。 使用如下命令运行客户端:
> java FileUtilClient fileName machineName savePath
这里fileName是要下载的文件名,machineName 是要下载的文件所在的机器(也是服务端所在的机器),savePath 是要将下载过来的文件保存的路径(带文件名)。如果全部通过的话,当客户端运行后,则这个文件将被下载到本地。