Java网络编程基础(七)— RMI分布式网络编程

Java RMI指的是远程方法调用(Remote Method Invocation)。它是一种机制,能够让在某个机器上的Java程序调用另一个机器上的Java程序的方法,用此方法调用的任何对象必须实现该远程接口。EJB就是建立在RMI基础之上的。

本节将讲解RMI的基本概念和系统原理,通过一些入门实例讲解RMI程序的开发步骤和编译运行方法。

RMI是RPC(远程过程调用)的Java版本的实现,远程过程调用是一种计算机分布式通信协议,包括Client/Server模型和分布式对象模型。

RPC(Remote Procedure Call)是计算机分布式通信协议。该协议允许运行与一台计算机的程序调用另一台计算机的子程序,而程序员无须为这个交互进行编程。

RPC总是由客户端对服务端发出一个执行若干过程的请求,并且利用客户端提供的参数,执行结果返回客户端。

为了允许不同的客户端均能访问服务端,许多RPC采用了接口描述语言IDL,方便跨平台的远程过程调用。

客户机/服务器模型Client/Server是分布式通信的一种形式。在这种新式中,客户机和服务器通信一遍交换信息。客户机/服务器的典型做法是使用底层套接字。Socket和NIO Socket编程就是套接字编程的两种Java实现

分布式对象模型

基于分布式对象的系统是一组对象的组合,这些对象以一种明确定义封装的接口把服务的请求着和服务的提供者分隔开。Java远程方法调用RMI和共用对象请求代理体系(CORBA)就是这种模型的例子。RMI仅适用于Java语言,CORBA支持多语言比较复杂。

Java远程方法调用RMI是Java编程语言里一种用于实现远程过程调用的应用程序编程接口,它使客户机上运行的程序可以调用远程服务器上的对象。RMI极大地依赖于接口。

RMI系统原理与开发步骤

RMI通信机制:RMI通常包括两个独立的程序:服务器和客户机程序

在与远程对象的通信过程中,RMI使用标准机制:Stub(存根)和Skeleton(框架)

在RMI分布式应用系统中,服务器和客户机之间的传递的Java对象必须是可序列化的对象,不可序列化的对象不能在对象流中进行传递。

RMI程序的实现步骤

将远程类的功能定义为Java接口。在Java中远程对象是实现远程接口的类的实例。

  • 远程接口必须声明为public。
  • 远程对象扩展java.rmi.Remote接口
  • 每个方法必须抛出java.rim.RemoteException
  • 任何作为参数或者返回值传送的远程对象的数据类型必须声明为远程接口类型。

RMI程序开发详解

  • 创建一个远程接口类
  • 创建一个接口实现类
  • 开发服务器端程序,启动远程接口服务
  • 开发客户端,访问远程接口服务

编译和运行的步骤如下:

  • 编译生成Stub和Skeleton
  • 启动RMI注册表
  • 启动服务器
  • 运行客户端

我们将通过一个开发一个HelloWorld入门程序来实践这些步骤。

实现类包括:

远程借口类ITestRemote

实现类TestRemote

服务器RMIServer

客户端RMIClient

调用关系

创建远程接口类ITestRemote.java

package org.test.rmi;

import java.rmi.Remote;import java.rmi.RemoteException;

/** * @author kucs * 定义远程接口,提供了dealData的方法 * 扩展了Remote * 远程接口类必须声明为public * 必须继承自java.rmi.Remote * 必须抛出一个java.rmi.RemoteExcetion例外 */public interface ITestRemote extends Remote {

	public String dealData(String world) throws RemoteException;

}

远程借口实现类TestRemote.java

package org.test.rmi;

import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;

/** * @author kucs * * 远程接口ITestRemote的实现类 * 扩展了UnicastRemoteObject, * 表示TestRemote类将用于创建一个单独的、不可复制的远程对象 * 它使用RMI默认的TCP的传送通道进行通信 */public class TestRemote extends UnicastRemoteObject implements ITestRemote {	/**	 * 	 */	private static final long serialVersionUID = 1L;

	protected TestRemote() throws RemoteException {		super();	}

	@Override	public String dealData(String world) throws RemoteException {		// TODO Auto-generated method stub		return "你好,"+world;	}

}

服务器RMIServer.java

package org.test.rmi;

import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.RMISecurityManager;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;

/** * @author kucs * 服务器需要做的3件事 * 1、创建RMI安全管理器RMISecurityManager的一个实例并安装它 * 2、创建远程对象的一个实例,在本实例中是TestRemote * 3、在RMI注册表中登记这个创建的对象 */public class RMIServer {

	public static void main(String[] args) {		// TODO 创建并安装安全管理器		if(System.getSecurityManager() == null){			System.setSecurityManager(new RMISecurityManager());		}

		try {			// TODO 创建远程对象			ITestRemote test = new TestRemote();			// TODO 启动注册表			LocateRegistry.createRegistry(1099);			//将名称绑定到对象			Naming.rebind("//localhost:1099/TestRemoteService", test);

			System.out.println("RMI服务器正在运行....");		} catch (RemoteException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (MalformedURLException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}

}

客户端RMIClient.java

package org.test.rmi;

import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.NotBoundException;import java.rmi.RMISecurityManager;import java.rmi.RemoteException;

/** * @author kucs * 客户端可以远程调用远程接口中指定的任何方法。 * 客户端必须从RMI注册表中获得指向该远程对象的引用 * 客户端需要做3件事 * 1、创建RMI安全管理器RMISecurityManager的一个实例并安装它 * 2、根据IP、端口号、服务名查找远程主机的服务并取得该服务的实例 * 调用该实例的本机接口方法 *  */public class RMIClient {

	public static void main(String[] args) {		// TODO 创建并安装安全管理器		if(System.getSecurityManager() == null){			System.setSecurityManager(new RMISecurityManager());;		}

		try {			ITestRemote test = (ITestRemote) Naming.lookup("rmi://localhost:1099/TestRemoteService");			System.out.println(test.dealData("我是客户端"));			System.out.println(test.dealData("中国"));		} catch (MalformedURLException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (RemoteException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (NotBoundException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}

}

编译并运行应用程序

为了运行应用程序,我们需要生成Stub和Skeleton,编译服务器和客户端程序,启动RMI注册表,最后是启动服务器和运行客户端程序

(1)编译生成Stub和Skeleton

为了生成Stub和Skeleton,我们使用rmi编译器编译实现类TestRemote.class。在项目功能目录的bin目录下,启动cmd。执行如下命令及结果如图示。这将生成TestRemote_Stub.class。TestRemote_Stub.class是一个客户机代理,TestRemote_Skel.class是一个服务器基干。        (2)启动RMI注册表,因为D我们在代码中添加了LocateRegitry.createRegistry(1099)语句,所以在启动服务器时自动自动RMI注册表

(3)编写安全许可文件

因为在服务器应用程序中正在使用RMI安全管理员,所以需要一个安全许可文件来与之匹配。安全许可文件policy.txt,我们将它保存在无包的目录下。

grant{	permission java.security.AllPermission "*:1000-9999","accept,connect,listen,resolve";};

(4)启动服务器命令java -Djava.security.policy=policy.txt org.test.rmi.RMIServer

(5)运行客户机命令java -Djava.security.policy=policy.txt org.test.rmi.RMIClient

运行结果

安全管理器

当没有写策略文件覆盖C:\Program Files\Java\jre1.6.0_05\lib\security里的java.policy时,调用

if(System.getSecurityManager()==null)

System.setSecurityManager(new RMISecurityManager());

系统会抛出异常 java.security.AccessControlException。
原因:每个Java应用都可以有自己的安全管理器,它是防范恶意攻击的主要安全卫士。安全管理器通过执行运行阶段检查和访问授权,以实施应用所需的安全策略,从而保护资源免受恶意操作的攻击。实际上,安全管理器根据Java安全策略文件决定将哪组权限授予类。然而,当不可信的类和第三方应用使用JVM 时,Java安全管理器将使用与JVM相关的安全策略来识别恶意操作。在很多情况下,威胁模型不包含运行于JVM中的恶意代码,此时Java安全管理器便不是必需的。当安全管理器检测到违反安全策略的操作时,JVM将引发 AccessControlException或SecurityException。
  在Java应用中,安全管理器是由System类中的方法setSecurityManager设置的。要获得当前的安全管理器,可以使用方法 getSecurityManager。 java.lang.SecurityManager类包含了很多checkXXXX方法,如用于判断对文件访问权限的checkRead(String file)方法。这些检查方法调用SecurityManager.checkPermission方法,后者根据安全策略文件判断调用应用是否有执行所请求的操作权限。如果没有,将引发SecurityException。  如果想让应用使用安全管理器和安全策略,可在启动JVM时设定-Djava.security.manager选项,还可以同时指定安全策略文件。如果在应用中启用了Java安全管理器,却没有指定安全策略文件,那么Java安全管理器将使用默认的安全策略,它们是由位于目录$JAVA_HOME/jre /lib/security中的java.policy定义的。 类装载器用Policy对象帮助它们决定,把一段代码导入虚拟机时应该给它们什么样的权限. 任何时候,每一个应用程序都只有一个Policy对象.
2.有关rmic的疑惑rmic是为客户端和服务器端生成相关的存根和骨架(实际上就是一种代理类),但是我们直接在javac java的过程就可以直接调用远程方法,好像没有rmic的调用。
原因:在jdk5.0以前的版本中,需要用rmic命令来为远程对象生成静态的代理类(包括存根和骨架类),而在jdk5.0中rmi框架会在运行的过程中自动为远程 对象生成动态代理类(包括存根和骨架类),从而更彻底的封装了rmi框架的实现细节。简化了rmi框架的使用方式。
3.registry本地注册表
实际上LocateRegistry.createRegistry(rmiPort)就是创建对远程或者本地注册表的本地引用,创建并导出Registry实例。

RMI实现了客户端与远程主机的调用,但是客户端和服务器必须都是基于Java语言的。如果要实现非Java语言之间的调用,就必须使用CORBA了。

时间: 2024-08-28 17:19:25

Java网络编程基础(七)— RMI分布式网络编程的相关文章

[.net 面向对象编程基础] (2) 关于面向对象编程

[.net 面向对象编程基础]  (2)  关于面向对象编程 首先是,面向对象编程英文 Object-Oriented Programming 简称 OOP 通俗来说,就是 针对对象编程的意思 那么问题来了什么是对象,不是“女盆友”“找对象”的这个对象.当然也可以理解“女盆友”也是“对象”的一种. 面向对象中的对象,指一切皆是对象 专业述语解释我们无视就好 只所有面向对象编程,是因为之前有一个面向过程编程 面向过程——是指把问题分解成步骤,一步一步实现 面向对象——是把构成问题的事务分成各个对象

java网络爬虫基础学习(一)

刚开始接触java爬虫,在这里是搜索网上做一些理论知识的总结 主要参考文章:gitchat 的java 网络爬虫基础入门,好像要付费,也不贵,感觉内容对新手很友好. 一.爬虫介绍 网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网下载网页,是搜索引擎的重要组成部分. 传统爬虫: 获得URL ->放入队列 ->抓取网页,分析信息 ->新的URL ->放入队列 ->抓取网页,分析信息... ->满足一定条件,停止. 聚焦爬虫: 根据一定的网页分析算法过滤与主题无关的链接

java网络爬虫基础学习(三)

尝试直接请求URL获取资源 豆瓣电影 https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=time&page_limit=20&page_start=0 浏览器打开该地址: 发现是这样的 在这里我们需要用java抓取电影的信息,首先要找到资源链接,浏览器右键->检查打开谷歌调试工具 我们可以看到下图 有很多的资源请求,在这里我是一个个搜索,看那个是电影信息的Headers 发

java网络爬虫基础学习(四)

jsoup的使用 jsoup介绍 jsoup是一款Java的HTML解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,可通过DOM,css以及类似于Jquery的操作方法来取出和操作数据. 主要功能 从一个URL,文件或字符串中解析出HTML. 使用DOM或css选择器来查找.取出数据. 可操作HTML元素.属性.文本. 直接请求URL 一开始直接使用jsonp的connect方法调用上节说的请求电影json数据会报错 错误如下: 这里不太清楚发生错误的原因,毕竟

java网络osi基础

java网络爬虫基础学习(二)

正则表达式 正则表达式写法 含义 \d 代表0-9的任意数字 \D 代表任何非数字字符 \s 代表空格类字符 \S 代表非空格类字符 \p{Lower} 代表小写字母[a-z] \p{Upper} 代表大写字母[A-Z] \p{Alpha} 代表字母 \p{Blank} 代表空格或制表符 HTTP状态码 分类 描述 1** 信息,服务器收到请求,需要请求者继续执行操作. 2** 成功,操作被成功接收并处理. 3** 重定向,需要进一步的操作已完成请求. 4** 客户端错误,请求包含语法错误或无法

Java网络编程和NIO详解开篇:Java网络编程基础

Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为我们拥有网络.网络是一个神奇的东西,它改变了你和我的生活方式,改变了整个世界. 然而,网络的无标度和小世界特性使得它又是复杂的,无所不在,无所不能,以致于我们无法区分甚至无法描述. 对于一个码农而言,了解网络的基础知识可能还是从了解定义开始,认识OSI的七层协议模型,深入Socket内部,进而熟练地

编程基础知识分享——编程开始之前

对我来说,编程这件事与其说是工作,不如说是兴趣.尽管和自己理想中的编程比,自己的所谓编程只不过是小打小闹,但是现在又有多少所谓的IT从业者是做真正的编程?产业化的今天,其实真的没有必要区分所谓的程序员和码农,每个人的路线和生活都不相同,也不可能相同,有人学编程只是为了一份工作,有人纯粹为了爱好.我的理解,无论出发点如何,只要尊重自己的选择,能够有所收获就够了. 这系列文章,是自己当初学习编程的一些方法和经验,整理了一下,可以分成几块记录.对于专业的程序员,我的经验并不适合,一些计算机专业出身的朋

第九章、shell脚本编程基础

第九章.shell脚本编程基础 本章内容 编程基础 脚本基本格式 变量 运算 条件测试 配置用户环境 编程基础 程序:指令+数据 程序编程风格: 过程式:以指令为中心,数据服务于指令 对象式:以数据为中心,指令服务于数据 shell程序:提供了编程能力,解释执行 程序的执行方式 计算机:运行二进制指令 编程语言: 低级:汇编 高级: 编译:高级语言-->编译器-->目标代码 java,C# 解释:高级语言-->解释器-->机器代码 shell, perl, python (系统后台