Java学习---面向对象的远程方法调用[RMI]
基础知识
分布式计算是一门计算机科学,它研究如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机进行处理,最后把这些计算结果综合起来得到最终的结果。 常见的分布式技术有:CORBA、DCOM和RMI。
其中,面向对象的远程方法调用(Remote Method Invocation,RMI)是Enterprise JavaBeans的支柱技术,是建立分布式Java应用程序的方便途径。RMI是Java的一组开发分布式应用程序的API,它使用Java语言接口定义了远程对象,集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。
通过RMI技术,使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方法调用。由于J2EE是分布式程序平台,它的RMI机制允许程序组件在不同操作系统之间的通信。比如,一个EJB可以通过RMI调用Web上另一台机器上的EJB远程方法。
在与远程对象的通信过程中,RMI使用标准机制:桩stub和架构skeleton。桩和架构是应用程序与系统其他部分的接口,它们使用RMI的rmic编译器产生。桩通常负责:初始化远程调用、序列化、远程方法调用、反序列化、远程方法调用完成等;构架负责:反序列化客户端参数、调用实际远程对象、序列化返回客户端参数。
RMI:面向对象的远程方法调用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。
开发工具包包括:
l java.rmi 客户端的rmi类,接口和异常
l java.rmi.sever 服务器端的rmi类,接口和异常
l java.rmi.registry 用于管理rmi命名服务的类
l java.rmi.dgc 用于管理分布式垃圾收集的类
l java.rmi.activation 用于按需激活的rmi服务的类
应用工具有:
n Rmic.exe 编译器,生成stub和skeleton
n rmiregistry.exe 为rmi提供命名服务的服务器,这项服务把名字和对象关联在一起
n rmid.exe 支持rmi激活框架的服务器
RMI实现流程
1、生成一个远程接口。所有的远程服务都需要继承Remote接口,该接口是一个不定义方法的标记接口,其全部内容只有一行public interface Remote{ }。
2、实现远程对象(服务器端程序) ,该类需要继承UnicastRemoteObject,并实现远程服务接口,而且还要使用RMISecurityManager;
3、生成桩程序和架构(服务器端程序) ;
4、编写服务器程序;
5、编写客户程序;
6、注册远程对象;
7、启动远程对象。
RMI在Windows下运行步骤,一共分为5步,分别是:
1.进入命令行窗口,进入自己项目的文件夹内
javac *.java
2.使用rmic serverName
产生serverName_Stub.class的文件
3.使用start rmiregistry [port]
4.输入java -Djava.security.policy=policyName serverName 运行服务器
5.如果成功另外打开一个命令行窗口进入自己项目文件夹
输入 java -Djava.security.policy=policyName clientName
(注意,有的时候要在后面加上参数localhost)
在RMI中,策略文件是一个文本文件,里面记录了一些对计算机资源访问的方式;比如对本地文件的访问控制,对端口的访问控制等。其文件后缀名为”*.policy”。系统策略文件的缺省位置为:java_jre.home/lib/security/java.policy (Solaris) ,java_jre.home\lib\security\java.policy (Windows) ,其格式为:
grant {
permission java.net.SocketPermission "*:1024-65535",
"connect,accept";
permission java.net.SocketPermission "*:80","connect";};
为了差别不同的安全策略,可建立自己的策略文件,如c:\MyPolicy.policy ,假设运行本地的TCP
2005端口可以连接、接受连接和解析,则内容为:grant {
permission java.net.SocketPermission
"localhost:2005","connect,accept,resolve";}
当运行RMI服务器程序时,指定了安全策略文件
java -Djava.security.policy=c:\MyPolicy.policy rmiServer
代码示例
编写RMI基础
//实现一个Remote接口
import java.rmi.*;
public interface Arith extends java.rmi.Remote{
int [] add(int a[], int b[])throws java.rmi.RemoteException;
}
远程接口定义说明了服务器提供的方法特性,包含了方法的名字和参数。这样的接口必备的特性:
l 必须声明为public
l 必须extends java.rmi.Remote接口
异常处理时,必须throws java.rmi.RemoteException
客户端:
//客户端,访问指定的服务器端
import java.rmi.*;
import java.net.*; public class ArithApp{
public static void main(String [] argv){
int a[] = {1,2,3,4,5,6,7,8,9,10}; //需要累加的数据
int b[] = {1,2,3,4,5,6,7,8,9,10}; int result [] = new int[10]; try{
Arith obj=(Arith)Naming.lookup("//localhost:3000/ArithServer"); //查找指定服务
result = obj.add(a,b); //调用RMI服务
}catch(Exception e){
System.out.println("ArithServer error" + e.getMessage());
e.printStackTrace();
} System.out.print("The sum=");
for(int i=0;i<result.length;i++){
System.out.println(result[i] + "");
}
System.out.println();
}
}
要运行这个程序,简单的方法是修改Java_JRE\lib\security\java.policy的内容
找到 grant{
在接下来的第一行添加以下语句,取消所有的安全限制。
permission java.security.AllPermission;
服务端:
//服务器端,实现一个RMI服务
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject; public class ArithImpl extends UnicastRemoteObject implements Arith{
private String objectName; public ArithImpl(String s)throws RemoteException{
super();
objectName = s;
} public int [] add(int a[], int b[]){ //服务内容是将两个组中对应元素累加
int c[] = new int[10]; for(int i=0; i<10; i++)
c[i] = a[i] + b[i]; return c;
} public static void main(String argv[]){
RMISecurityManager sm = new RMISecurityManager();
System.setSecurityManager(sm); //设置访问安全管理器 try{
ArithImpl obj = new ArithImpl("ArithServer"); //注册RMI服务名
Naming.rebind("//localhost:3000/ArithServer", obj); //绑定服务端口
System.out.println("ArithServer bound in registry");
}catch(Exception e){
System.out.println("ArithServer error" + e.getMessage());
e.printStackTrace();
}
}
}
程序运行:
运行一个RMI程序通常需要增加2个应用程序,分别是:
rmic 编译器,生成stub和skeleton
rmiregistry 一个为rmi提供命名服务的服务器,这项服务把名字和对象关联在一起
当需要运行一个RMI程序时,步骤为
•1、生成一个远程接口;
•2、实现远程对象(服务器端程序) ;
•3、生成桩程序和骨干网(服务器端程序) ;
•4、编写服务器程序;
•5、编写客户程序;
•6、注册远程对象;
•7、启动远程对象。 在WINDOWS下运行RMI,具体分为5步:
•1.进入命令行窗口,进入自己项目的文件夹内,编译所有的程序
javac *.java
•2.使用rmic对服务器端程序编译,生成一个serverName_Stub.class的桩文件
rmic serverName
•3.在服务器端启动rmi注册机,使用start rmiregistry
•4.输入java -Djava.security.policy=policyName serverName 运行服务器
•5.如果成功另外打开一个命令行窗口进入自己项目文件夹
•输入 java -Djava.security.policy=policyName clientName(注意,有的时候要在后面加上参数localhost)
在这里policyName是安全策略文件,它是一个文本文件,里面记录了一些对计算机资源访问的方式;比如对本地文件的访问控制,对端口的访问控制等。 后缀名需要是*.policy。
例如:
Grant codeBase “路径"
{
permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read";
permission java.net.SocketPermission "*:1024-65535", "connect,accept";
permission java.security.AllPermission;
};
授权了指定文件能读操作,socket的1024-65535端口可以连接和接受请求,以及所有安全机制都允许等控制策略。
简单的策略文件,假设文件名为: c:\MyPolicy.policy ,内容为:
grant {
permission java.net.SocketPermission "localhost:2005","connect,resolve";
}
执行时用命令,指定了安全策略文件
java -Djava.security.policy=c:\MyPolicy.policy rmiServer
如果要允许客户端的连接,还需要进一步修改policy文件,如
grant {
permission java.net.SocketPermission "localhost:1023-65535","connect,accept, resolve";
}
表示本地的1023-65535端口,都可以进行连接、接受连接请求、解析的任务。
【更多参考】
有意义的RMI程序
在以前的多线程程序中,要求进行1-10000数字的累加,当时建议采用多线程的方法,每个线程分别执行一定数值范围的累加,最后将线程结果累加得到所希望的答案。现在利用RMI将累加过程分散到多台计算机上,实现分布式的累加。
客户端:单个服务器
//客户端,访问单一指定的服务器端
import java.rmi.*;
import java.net.*; public class AddApp{
public static void main(String [] argv){
int a = 1; //需要累加的数据
int b = 10000; int result = 0; try{
Add obj=(Add)Naming.lookup("//IP1:3000/AddServer"); //查找指定服务
result = obj.add(a,b); //调用RMI服务
}catch(Exception e){
System.out.println("AddServer error" + e.getMessage());
e.printStackTrace();
} System.out.print("The sum=" + result);
System.out.println();
}
}
客户端:多个服务器
//客户端,访问多个指定的服务器端
import java.rmi.*;
import java.net.*; public class AddApp implements Runnable{
int start, end;
String server;
int result=0; public AddApp(int start, int end, String server){
this.start = start;
this.end = end;
this.server = server;
} Public void run(){
try{
Add obj=(Add)Naming.lookup(“//” + server + ":3000/AddServer"); //查找指定服务
result = obj.add(a,b); //调用RMI服务
}catch(Exception e){
System.out.println("AddServer error" + e.getMessage());
e.printStackTrace();
} System.out.print("The sum=" + result);
System.out.println(); }
public static void main(String [] argv){
AddApp ap1 = new AddApp(1,2000, IP1);
AddApp ap2 = new AddApp(2001,4000, IP2);
AddApp ap3 = new AddApp(4001,6000, IP3);
AddApp ap4 = new AddApp(6001,8000, IP4);
AddApp ap5 = new AddApp(8001,10000, IP5); ap1.start(); ap2.start(); ap3.start(); ap4.start(); ap5.start(); ap1.join(); ap2.join(); ap3.join(); ap4.join(); ap5.join(); System.out.print("The sum=" + (ap1.result + ap2.result + ap3.result + ap4.result + ap5.result));
System.out.println();
}
}
服务端:
//服务器端,实现一个RMI服务
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject; public class AddImpl extends UnicastRemoteObject implements Arith{
private String objectName; public ArithImpl(String s)throws RemoteException{
super();
objectName = s;
} public int add(int start, int end){ //服务内容是将两个组中对应元素累加
int c = 0; for(int i=start; i<=end; i++) //考虑如何实现多线程累加
c = c + i; return c;
} public static void main(String argv[]){
RMISecurityManager sm = new RMISecurityManager();
System.setSecurityManager(sm); //设置访问安全管理器 try{
AddImpl obj = new AddImpl("AddServer"); //注册RMI服务名
Naming.rebind("//不同的IP:3000/AddServer", obj); //绑定服务端口
System.out.println("AddServer bound in registry");
}catch(Exception e){
System.out.println("AddServer error" + e.getMessage());
e.printStackTrace();
}
}
}
Java学习---面向对象的远程方法调用[RMI]的更多相关文章
- java学习面向对象之异常之一
一.异常的概述: 什么是异常?在我们编写java程序的时候,会出现一些问题,比如内存溢出啊或者数组索引超出最大索引啊,这些编程当中出现的这些个问题就是异常.但是异常也分为可以处理的和不可以处理的.比如 ...
- java学习面向对象之匿名内部类
之前我们提到“匿名”这个字眼的时候,是在学习new对象的时候,创建匿名对象的时候用到的,之所以说是匿名,是因为直接创建对象,而没有把这个对象赋值给某个值,才称之为匿名. 匿名对象回顾: class N ...
- java学习面向对象之内部类
什么是面向对象内部类呢?所谓的内部类,即从字面意义上来理解的话,就是把类放到类当中. 那么内部类都有什么特点呢? 1.内部类可以访问包裹他的类的成员. 2.如果包裹他的类想访问被其包裹的类的话就得实例 ...
- java学习面向对象之多态
如何理解多态,让我们举个例子来描述一下,因为单纯的说多态大家可能不理解: abstract class Animal { ; abstract void eat(); public void run( ...
- java学习面向对象之接口
上一节当中我们说道抽象类,抽象类当中的方法可以是抽象的也可以是非抽象的,那么当抽象类中所有方法都是抽象的时候,我们就可以把它重新定义为接口.代码示例: abstract class Animal { ...
- java学习面向对象之父子构造函数初始化
在之前讲到java面向对象继承的时候,我们只讲到了两个比较重要的知识点,一个是父子类当中有同名的成员变量,这个时候,我们引入了super这个关键字来区分这两个同名成员变量,除此之外,我们还讲到了父子同 ...
- java学习面向对象之继承
在我们编写程序的过程当中,会遇到这种情况: 比如现在有一个狗,他的功能有跑,有跳,有吃,有叫,属性有雌雄,大小,颜色等等,同时现在我们也有一个猫,上述功能她也有.这个时候我们写代码的时候,就得分别把上 ...
- java学习--面向对象
对象及类的概念 对象是java程序的核心,在java程序中“万事万物皆对象” 对象可以看成是属性和方法的封装体 类是用来创建同一类型的对象的模板,在一个类中定义了该类对象所应具有的属性和方法 J2SD ...
- Java学习 面向对象(下)——Java疯狂讲义th4
面向对象(下) [TOC] 包装类 通过包装类可以把8个基本类型的值包装成对象使用. 自动拆箱.自动装箱 把字符串类型值转换成基本类型的值: 包装类的 parseXxx(String s)静态方法 包 ...
随机推荐
- ie和火狐事件addEventListener()及attachEvent()区别分析
Mozilla中: addEventListener的使用方式: target.addEventListener(type, listener, useCapture); target: 文档节点.d ...
- Spring mvc解析
方案时间 ,写代码时间 ,解决技术难点时间 , 自测时间,解决bug时间 , 联调时间 ,数据库优化,代码走查1个接口:2个小时 把那个字段再复原回来,不然兼容性不强还有一个刷数据的接口 public ...
- iOS开源项目周报0323
由OpenDigg 出品的iOS开源项目周报第十三期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等. CHIPag ...
- Spring注解_详解
@Autowired 注释 将 @Autowired 注释标注在成员变量上 import org.springframework.beans.factory.annotation.Autowire ...
- WCF 之部署(2010版本之上)
首先,在WIN系统下打开IIS,每种操作系统是不同的,请不要弄混. 1.控制面板---左下放的程序---启用或关闭Windows功能,如图: 2.找到Internet Information Serv ...
- 如鹏网学习笔记(九)JavaScript
JavaScript笔记 一.JavaScript简介 1,JavaScript是一种计算机编程语言,可以像等其他编程语言那样定义变量,执行循环等. 2,JavaScript代码主要执行在浏览器上,为 ...
- 访问Django项目出现DisallowedHost at / Invalid HTTP_HOST header问题
闲来无事,想玩玩django,源码安装碰到了一堆乱七八糟依赖性问题,耗费一下午的时间总算是在ubuntu14.04上搭建好了python3+django2开发环境, 心血来潮,创建了一个django项 ...
- [javaSE] 面向对象(Object类toString)
每一个对象,都有一个在内存中的地址哈希值,这个哈希值是十六进制的 调用Object对象的hashCode()方法,返回这个对象的哈希值 调用Integer.toHexString()方法,转换十六进制 ...
- src/main/resources文件夹
Error starting ApplicationContext. To display the auto-configuration report re-run your application ...
- [翻译]Review——How JavaScript works:The building blocks of Web Workers
原文地址:https://blog.sessionstack.com/how-javascript-works-the-building-blocks-of-web-workers-5-cases-w ...