RMI

RMI基础

一 简介

1.RMI,Remote Method Invocation,即远程方法调用,是Java远程访问的重要规范之一,允许java程序直接调用远程java方法,就像调用该本地java方法一样。Java RMI不是什么新技术,但却是非常重要的底层技术。大名鼎鼎的EJB都是建立在RMI基础之上的,现在还有一些开源的远程调用组件,其底层技术也是RMI。

2.远程访问:网络中的一台计算机,可以使用另一台计算机的服务、对象或方法,效果就像使用本地的服务、对象和方法一样。通过远程访问,程序可以在物理位置不同的机器上运行,从而实现分布式计算

3.RMI是纯JAVA实现的,无需其他额外的支持,实现RMI调用的客户端程序和被调用的方法,都是纯JAVA代码,因此实现起来很简单。在JDK1.5发布以前,读者需要手动使用rmic命名(在JDK安装路径的bin路径下)来编译远程服务类,从而为远程服务类生成静态的Stub和Skeleton类。在JDK1.5及以后的版本中,采用了动态方式来自动生成Stub和Skeleton类,简化了RMI编程。

4.RMI可以说是Java最早实现的远程访问技术,而且性能十分优秀,一直是其他Java EE规范的实现基础。掌握RMI的理论和编程方法,对于开发者更好的理解EJB等分布式组件的底层实现具有重要意义。

RMI其实是对Java Socket网络通信的高度封装,如果应用程序提供基于Socket的网络通信功能,都可以考虑使用RMI实现。

当使用原生Socket进行网络编程时,有两个重要的问题:

网络信息的交换问题:需要采用合适的IO流进行网络读写,如果需要进行复杂的网络信息交换,往往还要封装自己的通信协议

多线程问题:服务器和客户端的网络程序,两端都要采用多线程功能。编写多线程就不可避免的要处理线程同步、线程安全、死锁等棘手问题。

而使用RMI来开发,以上的问题就会迎刃而解,可以简化开发难度,提高开发效率。

二 基本原理

JDK1.5以后动态生成Stub对象来作为客户端调用远程方法的代理,由于Stub是根据远程服务类来动态生成的,因此当远程服务端的服务类改变时,系统会动态的改变所生成的Stub对象。

由图中可以看出,当客户端面向远程服务接口调用远程方法后,会有以下几个步骤:

(1)本地客户端调用远程服务对象的方法,实际上是调用Stub对象的方法。

(2)Stub对象实际上是远程服务对象在客户端的代理。Stub对象会对请求进行编码,保证远程调用请求可以在网络上传输,所以要求调用远程方法的所有参数都是可序列化的。

(3)通过底层网络传输将请求传递到Skeleton

(4)Skeleton对请求进行解码,将请求转换为满足远程服务对象要求的请求。

(5)Skeleton将解码后的请求发送到远程服务对象,让远程服务对象来负责处理调用请求。

(6)Skeleton收到远程服务对象的执行结果,再次对执行结果进行编码,这就要求RMI中的方法返回值都是可序列化的。

(7)通过底层网络传输将结果送到Stub。

(8)Stub解码处理结果

(9)Stub将解码后的符合本地客户端要求的结果送到本地客户端

(10)本地客户端收到执行结果

三 动手

开发RMI服务器

Step1:RMI需要通过远程接口暴露服务,因此所有向北客户端调用的方法都必须在Remote接口里声明,否则无法调用。

远程接口如下:

public interface Server extends Remote{

String helloWorld(String name) throws RemoteException;

Person getPerson(String name,int age) throws RemoteException;

}

注意:远程接口必须集成java.rmi.Remote接口。所有在Remote接口里声明的方法都应该抛出RemoteException异常。

这是因为远程接口中的方法会通过网络传输,而网络传输又是不可靠的,因此都要抛出一个异常。

Step2:定义一个简单的Person类

public class Person implements Serializable{

private String name;

private int age;

...//省略getter/setter等方法

}

Step3:远程服务实现类

public class ServerImpl extends UnicastRemoteObject implements Server{

//由于默认构造器必须声明抛出RemoteException

//因此此处必须显示定义该构造器

public ServerImpl() throws RemoteException{}

@Override

public String helloWorld(String name) throws RemoteException {

return name+", hello";

}

@Override

public Person getPerson(String name, int age) throws RemoteException {

return new Person(name,age);

}

//下面是服务类的本地方法,不会暴露为远程服务

public void info(){

System.out.println("这是本地方法");

}

//提供程序入口,将远程类实例绑定为本机的服务

public static void main(String[] args) throws Exception{

try {

Server imp=new ServerImpl();

LocateRegistry.createRegistry(8888);

Naming.rebind("rmi://localhost:8888//myTest",imp);

}catch (RemoteException e) {

System.out.println("创建远程对象发生异常!");

e.printStackTrace();

} catch (AlreadyBoundException e) {

System.out.println("发生重复绑定对象异常!");

e.printStackTrace();

} catch (MalformedURLException e) {

System.out.println("发生URL畸形异常!");

e.printStackTrace();

}

}

}

注意:远程服务类必须继承UnicastRemoteObject,并实现Remote接口,所有继承java.rmi.server.UnicastRemoteObject的对象可以暴露远程服务。

开发RMI客户端

public class RMIClient {

public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {

Server ser=(Server) Naming.lookup("rmi://127.0.0.1:8888/Server");

System.out.println(ser.helloWorld("lyy"));

System.out.println(ser.getPerson("lyy",20));

}

}

运行时注意,先运行服务器端,在运行客户端。

结果:

lyy, hello

Person [name=lyy, age=20]

四 同时作为客户端和服务器的RMI程序

很多程序,客户端需要调用服务器的远程方法,服务器端也要反过来调用客户端的远程方法,这样才能形成一个完整的网络通信程序。但是服务器端和客户端存在很多区别,因此服务器端不大可能直接调用远程客户端的方法,只能先由客户端调用服务器端的远程方法,这样服务器端才可以回过来调用远程客户端的方法,这种调用方法也被成为回调(callback)。

举例:聊天窗口

客户端

建包client

Step1:由于客户端也要被远程服务器回调,因此客户端程序也要提供远程服务。

public interface Client extends Remote {

void showDialog(String msg) throws java.rmi.RemoteException;

}

Step2:服务实现

public class RMIClient implements Client{

public static void main(String[] args) throws Exception{

Client client = new RMIClient();

UnicastRemoteObject.exportObject(client);

Server stub=(Server)Naming.lookup("rmi://127.0.0.1:8888/Server");

BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

String line=null;

while((line=br.readLine())!=null){

//调用远程方法时,将自身作为参数

stub.hello(client,line);

}

}

@Override

public void showDialog(String msg) throws RemoteException {

System.out.println(msg);

}

}

服务器端程序

建包server

Step1:接口

public interface Server extends Remote{

//定义允许远程调用的方法

void hello(Client client,String saying) throws Exception;

}

Step2:实现

public class ServerImpl extends UnicastRemoteObject implements Server{

public ServerImpl() throws RemoteException{}

//定义一个List保存所有连接进来的客户

static List<Client> users=new ArrayList<Client>();

//提供程序入口,将远程类实例绑定为本机的服务

public static void main(String[] args) throws Exception{

try {

Server imp=new ServerImpl();

LocateRegistry.createRegistry(8888);

Naming.rebind("rmi://127.0.0.1:8888/Server",imp);

}catch (RemoteException e) {

System.out.println("创建远程对象发生异常!");

e.printStackTrace();

} catch (AlreadyBoundException e) {

System.out.println("发生重复绑定对象异常!");

e.printStackTrace();

} catch (MalformedURLException e) {

System.out.println("发生URL畸形异常!");

e.printStackTrace();

}

}

@Override

public void hello(Client client, String saying) throws Exception {

if(!users.contains(client)){

users.add(client);

}

try{

java.util.Date now=new java.util.Date();

String msg=now+saying;

for(Client c:users){

//回调远程客户端方法

c.showDialog(msg);

}

}catch(RemoteException ex){

users.remove(client);

}

}

}

时间: 2024-08-07 00:18:18

RMI的相关文章

Java——RMI

之前分布式系统调用用的是比较老的EJB,当时还是作为服务调用方,去调用别的系统的服务.最近发现新公司里面,用的是RMI,查了下发现EJB的底层实现就是RMI,也算是熟悉了... 一,使用JDK 中的RMI实现服务发布和引用 服务端接口: /** * Created by LiuHuiChao on 2016/11/18. */ public interface UserInfoService extends Remote{ /** * 定义远程接口,必须继承Remote接口, * 其中所有需要远

Java学习笔记之RMI远程方法调用

RMI 应用通常有两个分开的程序组成,一个服务端程序和一个客户端程序.一个典型的服务端程序创建一些远程对象,使得对这些远程对象的引用可以被访问,等待客户端调用这些远程对象提供的方法.一个典型的客户端程序获取远程引用,指向一个或者多个服务端上的远程对象,然后调用这些远程对象所提供的方法.通常我们称这为分布式对象应用程序. 3.1 RMI的工作方式 分布式对象应用程序需要做的事情: l 查找(定位)远程对象. 应用程序可以使用各种不同的机制取得远程对象的引用.比如应用程序可以通过 RMI 提供的简单

【J2EE浅析】——RMI

一.RMI概述 RMI(Remote Method Invoke)-远程调用方法,是java的一组用户开发分布式应用程序的API.通过该RMI机制,可以实现程序组件在不同操作系统之间的通信.它是一种被EJB使用的更底层的协议,比如:一个EJB可以通过RMI调用Web上另一台机器上的EJB远程方法. RMI使用了序列化方式在客户端和服务器端传递数据, 这样,我们就可以在远程方式下编写和使用对象,而不必知道它们实际上是远程的对象. 目标: *无缝地支持在不同的Java虚拟机上的远程对象的使用. *支

idea启动tomcat服务失败 SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild:

我的工程是从eclipse生成的,个人习惯用idea开发.重复了一遍以往正常的不能再正常了的导入配置,结果遇到了如下问题: SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start comp

java RMI入门指南

感觉这篇文章不错,直接转了 RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体如今它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方式之中的一个.事实上它能够被看作是RPC的Java版本号.可是传统RPC并不能非常好地应用于分布式对象系统.而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信.实现远程对象之间的无缝远程调用. RMI眼下使用Java远程消息交换协议JRMP

Spring 配置RMI远程调用

项目中遇到了跨系统调用,当初想的是用webservice或者是hessian.但是这个接口用到的并不多,而且是一个非常简单的方法.所有便想到了RMI.在spring中 实现RMI非常简单. 暴露服务: 引用服务: 在spring中 ,spring已经集成了rmi服务,无需再导入任何jar包.非常方便,简单.

RMI的实现

RMI,即Remote Method Invoke(远程方法调用).RMI最初在1.1被引入Java平台中,它为Java开发者提供了一种强大的方法来实现Java程序间的交互.在RMI之前,对于Java开发者来说,远程调用的唯一选择是CORBA或者手工编写Socket程序. 关于RMI框架的基本原理如下图:

Metasploit溢出java RMI SERVER命令执行漏洞

利用java RMI SERVER命令执行漏洞,获得目标主机root权限. Java RMI Server的RMI注册表和RMI激活服务的默认配置存在安全漏洞,可被利用导致代码执行. 一.利用nmap工具扫描目标主机 1.1使用nmap命令对目标主机进行扫描.单击桌面空白处,右键菜单选择"在终端中打开". 1.2在终端中输入命令"nmap –sV  192.168.1.3",对目标主机进行端口扫描,发现开放1099端口. 1.3在终端中输入命令"msfco

JAVA RMI分布式原理和应用

RMI(Remote Method Invocation)是JAVA早期版本(JDK 1.1)提供的分布式应用解决方案,它作为重要的API被广泛的应用在EJB中.随着互联网应用的发展,分布式处理任务也随之复杂起 来,WebService也得到普遍的推广和应用.        在某些方面,例如跨语言平台的分布式应用,RMI就显得力不从心了.在实际的应用中,是采用WebService还是传统的RMI来实现?这是一个需要权衡的问题,两者的比较如下所述:        1. 比起WebService,它

java基础十一[远程部署的RMI](阅读Head First Java记录)

方法的调用都是发生在相同堆上的两个对象之间(同一台机器的Java虚拟机),如果想要调用另一台机器上的对象,可以通过Socket进行输入/输出. 远程过程调用需要创建出4种东西:服务器.客户端.服务器辅助设施.客户端辅助设施 RMI Java的JMI提供客户端和服务器端的辅助设施对象(stub和skeleton,现在实际只用stub文件,客户端和服务端用一个) 辅助设施是实际执行通信的对象,他会让客户端感觉在调用本机,实际上辅助设施类似于代理,将客户端传送的信息通过Socket连接发送给服务端辅助