一. RMI介绍
远程方法调用(Remote Method Invocation, RMI )是Sun公司规定的允许在不同的JAVA虚拟机之间进行对象间通信的一种规范。在RMI中,JVM可以位于一个或多个计算机上,其中一个JVM可以调用存储在另一个JVM中的对象方法。这就使得应用程序可以远程调用其他对象方法,从而达到分布式计算的目的,以共享各个系统的资源和处理能力。
除了RMI外,基于JAVA的实现不同JAVA虚拟机上的应用程序之间通信技术主要有两种:套接字和JAVA消息服务 (JMS)。
使用套接字是实现程序间通信的最为灵活和强大的方式。但是它必须通过应用级协议进行通信,要求应用程序之间使用同样的协议,并且要求设计通信过程中的错误判断等。
JMS和RMI的区别在于,采用JMS服务,对象是物理上被异步地从网络的某个JVM上直接移动到另一个JVM上。而RMI对象是绑定在本地JVM上,只有函数参数和返回值是通过网络传送的。
二. RMI开发应用程序的一般步骤:
- 定义远程接口
- 实现这个远程接口
- 生成stub(客户代理) 和 skeleton(服务器实体)
- 编写使用远程对象的客户程序。
- 启动注册表并登记远程对象
- 运行服务器和客户程序
三. Eclipse 编程环境搭建
安装RMI插件:
1、首先下载Eclipse的RMI开发插件 下载地址:http://www.genady.net/rmi/v20/downloads.html
2、解压缩将net.genady.rmi_2.5.0文件夹下的两个文件拷贝到eclipse安装目录下,覆盖同名的两个文件夹
3、重启eclipse即可在快捷栏看到RMI插件标志
四. 简单实例
1. 定义远程接口
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package RMIinterface; 2 3 import java.rmi.Remote; 4 import java.rmi.RemoteException; 5 6 public interface iHello extends Remote{ 7 public String sayHello ()throws RemoteException; 8 }
创建一个远程接口时,必须遵守下列规则:
1. 远程接口必须为public
2. 远程接口必须继承java.rmi.Remote 除应用程序本身有关异常外,
3. 远程接口中的每个方法都必须在自己的 throws中声明java.rmi.RemoteException
4. 作为参数或返回值传递的一个远程对象,必须声明为远程接口,不可 声明为实现类。
2. 实现这个接口
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package rmiIMP; 2 3 import java.rmi.RemoteException; 4 import java.rmi.server.UnicastRemoteObject; 5 6 import RMIinterface.iHello; 7 8 public class rmiIMP extends UnicastRemoteObject implements iHello{ 9 10 private static final long serialVersionUID = 1L; 11 12 public rmiIMP() throws RemoteException { 13 } 14 15 @Override 16 public String sayHello() throws RemoteException { 17 return "hello zhang"; 18 } 19 20 21 22 }
3. 构建服务器程序
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package rmiServer; 2 3 import java.net.MalformedURLException; 4 import java.rmi.AlreadyBoundException; 5 import java.rmi.Naming; 6 import java.rmi.RemoteException; 7 import java.rmi.registry.LocateRegistry; 8 9 import rmiIMP.rmiIMP; 10 import RMIinterface.iHello; 11 12 public class helloServer { 13 14 public static void main(String[] args) { 15 16 try { 17 iHello ihello = new rmiIMP(); 18 LocateRegistry.createRegistry(1099); 19 Naming.bind("rmi://localhost:1099/iHello",ihello); 20 21 22 } catch (RemoteException e) { 23 System.out.println("创建远程对象异常!"); 24 e.printStackTrace(); 25 } catch (MalformedURLException e) { 26 System.out.println("URL异常!"); 27 e.printStackTrace(); 28 } catch (AlreadyBoundException e) { 29 System.out.println("绑定异常!"); 30 e.printStackTrace(); 31 } 32 33 } 34 35 }
4.构建客户程序
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package rmiClient; 2 3 import java.net.MalformedURLException; 4 import java.rmi.Naming; 5 import java.rmi.NotBoundException; 6 import java.rmi.RemoteException; 7 8 import RMIinterface.iHello; 9 10 public class helloClient { 11 12 public static void main(String[] args) { 13 try { 14 iHello ihello = (iHello) Naming.lookup("rmi://localhost:1099/iHello"); 15 System.out.println(ihello.sayHello()); 16 17 18 } catch (MalformedURLException e) { 19 e.printStackTrace(); 20 } catch (RemoteException e) { 21 e.printStackTrace(); 22 } catch (NotBoundException e) { 23 e.printStackTrace(); 24 } 25 } 26 27 }
5.进入cmd,对所在位置的接口进行编译。
6.右键该项目,打开运行配置窗口,找到RMI VM Properties后,对java.security.policy与java.rmi.server.codebase进行配置
7. 然后在RMI Application方式下运行服务器程序,在Java Application方式下运行客户程序。
五. 结合XML编程的RMI实例——航班信息查询(通过查询目的地显示到达该目的地的所有航班信息)
1.编写多个XML文件,每个XML文件表示一所航空公司。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 <?xml version="1.0" encoding="GB2312"?> 2 <!DOCTYPE 航班列表 [ 3 <!ELEMENT 航班列表 (航班)*> 4 <!ELEMENT 航班 (编号,始发地,目的地,出发时间,到达时间)> 5 <!ELEMENT 编号 (#PCDATA)> 6 <!ELEMENT 始发地 (#PCDATA)> 7 <!ELEMENT 目的地 (#PCDATA)> 8 <!ELEMENT 出发时间 (#PCDATA)> 9 <!ELEMENT 达到时间 (#PCDATA)> 10 ]> 11 <航班列表> 12 <航班> 13 <编号>101</编号> 14 <始发地>武汉</始发地> 15 <目的地>北京</目的地> 16 <出发时间>2016-05-02 16:30</出发时间> 17 <到达时间>2016-05-02 19:25</到达时间> 18 </航班> 19 <航班> 20 <编号>102</编号> 21 <始发地>深圳</始发地> 22 <目的地>成都</目的地> 23 <出发时间>2016-05-03 10:15</出发时间> 24 <到达时间>2015-05-03 14:00</到达时间> 25 </航班> 26 <航班> 27 <编号>103</编号> 28 <始发地>北京</始发地> 29 <目的地>天津</目的地> 30 <出发时间>2016-05-02 16:30</出发时间> 31 <到达时间>2016-05-02 19:25</到达时间> 32 </航班> 33 </航班列表>
2.定义远程接口
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package searchF; 2 3 import java.rmi.Remote; 4 import java.rmi.RemoteException; 5 6 public interface SearchFlight extends Remote{ 7 public void SearchF()throws RemoteException; 8 9 }
3.实现这个远程接口
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package searchIMP; 2 3 import java.io.File; 4 import java.rmi.RemoteException; 5 import java.rmi.server.UnicastRemoteObject; 6 import java.util.Scanner; 7 8 import javax.xml.parsers.DocumentBuilder; 9 import javax.xml.parsers.DocumentBuilderFactory; 10 11 import org.w3c.dom.Document; 12 import org.w3c.dom.Node; 13 import org.w3c.dom.NodeList; 14 15 import searchF.SearchFlight; 16 17 public class searchFIMP extends UnicastRemoteObject implements SearchFlight{ 18 19 20 private static final long serialVersionUID = 1L; 21 22 public searchFIMP() throws RemoteException { 23 super(); 24 } 25 26 @Override 27 public void SearchF() throws RemoteException { 28 try { 29 //使用DOM解析XML文件 30 //获得一个XML文件的解析器 31 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 32 //解析XML文件生成DOM文档的接口类,以便访问DOM 33 DocumentBuilder builder = factory.newDocumentBuilder(); 34 System.out.print("请输入您要查询的目的地:"); 35 Scanner scan = new Scanner(System.in); 36 String city = scan.nextLine(); 37 scan.close(); 38 int[] temp = new int[3]; 39 int[] count = new int[3]; 40 for(int k = 0 ; k < 3; k++){ 41 String airport = "flight"+ (k+1) +".xml"; 42 //Document接口描述了对应于整个XML文件的文档树 43 Document document = builder.parse(new File(airport)); 44 //获取“航班”元素的子节点列表 45 NodeList nodelist = document.getElementsByTagName("航班"); 46 temp[k] = nodelist.getLength(); 47 count[k] = 0; 48 for (int i = 0; i < nodelist.getLength(); i++) { 49 NodeList nl = nodelist.item(i).getChildNodes(); 50 if(getFlight(nl, city)) { 51 if(k == 0 && count[k] == 0) 52 System.out.println("到达该目的地的所有航班信息如下:"); 53 count[k] ++; 54 for (int j = 0; j < nl.getLength(); j++) { 55 Node cnode = nl.item(j); 56 if (cnode.getNodeType() == Node.ELEMENT_NODE) { 57 System.out.println(" -->" + cnode.getNodeName() + ": " + cnode.getTextContent()); 58 } 59 } 60 System.out.println(); 61 }else 62 temp[k]--; 63 } 64 } 65 if(temp[0] + temp[1] + temp[2] == 0 ) 66 System.out.println("没有到达该目的地的航班信息!"); 67 else{ 68 int sum = count[0]+count[1]+count[2]; 69 System.out.println("共有"+ sum + "条航班信息!"); 70 } 71 } 72 catch (Exception e) { 73 e.printStackTrace(); 74 } 75 } 76 77 //判断所当前航班的目的地城市是否为所查询城市,若是,则返回true 78 public static boolean getFlight(NodeList nodelist, String str) { 79 boolean temp = false; 80 for (int i = 0; i < nodelist.getLength(); i++) { 81 Node node = nodelist.item(i); 82 if (node.getNodeType() == Node.ELEMENT_NODE) { 83 if (node.getTextContent().equals(str) && node.getNodeName().equals("目的地")) { 84 temp = true; 85 } 86 } 87 } 88 return temp; 89 } 90 }
4.构建服务器程序
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package searchServer; 2 3 import java.net.MalformedURLException; 4 import java.rmi.AlreadyBoundException; 5 import java.rmi.Naming; 6 import java.rmi.RemoteException; 7 import java.rmi.registry.LocateRegistry; 8 import searchF.SearchFlight; 9 import searchIMP.searchFIMP; 10 11 public class searchFServer { 12 13 public static void main(String[] args) { 14 try { 15 searchFIMP imp = new searchFIMP(); 16 LocateRegistry.createRegistry(1099); 17 Naming.bind("rmi://localhost:1099/searchFIMP",imp); 18 19 } catch (RemoteException e) { 20 System.out.println("创建远程对象异常!"); 21 e.printStackTrace(); 22 } catch (MalformedURLException e) { 23 System.out.println("URL异常!"); 24 e.printStackTrace(); 25 } catch (Exception e){ 26 e.printStackTrace(); 27 } 28 } 29 }
5.构建客户程序
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package searchClient; 2 3 import java.net.MalformedURLException; 4 import java.rmi.Naming; 5 import java.rmi.NotBoundException; 6 import java.rmi.RemoteException; 7 import searchF.SearchFlight; 8 9 public class searchFClient { 10 11 public static void main(String[] args) { 12 try { 13 SearchFlight searchF = (SearchFlight) Naming.lookup("rmi://localhost:1099/searchFIMP"); 14 searchF.SearchF(); 15 16 17 } catch (MalformedURLException e) { 18 e.printStackTrace(); 19 } catch (RemoteException e) { 20 e.printStackTrace(); 21 } catch (NotBoundException e) { 22 e.printStackTrace(); 23 } 24 } 25 26 }
6.以RMI Application方式运行服务器程序,正常运行客户端程序,运行结果如下: