-
RMI远程调用
2020-12-12 19:46:12前言 前段时间查代码问题时发现应用代码中接口变量指向的实例对象是Naming.lookup(xx) 返回的,但在...RMI远程调用步骤: 1,客户对象调用客户端辅助对象上的方法 2,客户端辅助对象打包调用信息(变量,方法名),通过前言
前段时间查代码问题时发现应用代码中接口变量指向的实例对象是Naming.lookup(xx) 返回的,但在整个应用里又找不到这个接口的实现,当时就特别好奇,后面才了解到原来就是远程调用(RMI),今天就简单了解了一下。
RMI :远程方法调用(Remote Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法
RMI远程调用步骤:
1,客户对象调用客户端辅助对象上的方法
2,客户端辅助对象打包调用信息(变量,方法名),通过网络发送给服务端辅助对象
3,服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象
4,调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象
5,服务端辅助对象将结果打包,发送给客户端辅助对象
6,客户端辅助对象将返回值解包,返回给客户对象
7,客户对象获得返回值
代码实现
package com.rmi; import java.rmi.Remote; import java.rmi.RemoteException; //创建远程方法接口,该接口必须继承自Remote接口 //Remote 接口是一个标识接口,用于标识所包含的方法可以从非本地虚拟机上调用的接口 public interface Hello extends Remote { //由于远程方法调用的本质依然是网络通信,只不过隐藏了底层实现,网络通信是经常会出现异常的, //所以接口的所有方法都必须抛出RemoteException以说明该方法是有风险的 public String sayHello(String name) throws RemoteException; }
package com.rmi; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; //实际传输的时候就是这个类的实例,所以要指定 serialVersionUID ,不想序列化的字段加关键词 transient即可 public class HelloImpl extends UnicastRemoteObject implements Hello { private static final long serialVersionUID = 1L; public HelloImpl() throws RemoteException { } @Override public String sayHello(String name) throws RemoteException { return "Hello," + name; } }
package com.rmi.test; import com.rmi.Hello; import com.rmi.HelloImpl; import java.io.IOException; import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.Socket; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RMISocketFactory; public class ServerTest { public static void main(String[] args) { test2(); } //在jdk的bin下有个rmiregistry.exe 需要在项目的classpath下(比如target/classes下)运行该程序 //rmiregistry 1099 (这个是默认端口) 可以使用 start rmiregistry 命令在后台运行 private static void test1(){ try { Hello hello = new HelloImpl(); Naming.rebind("user", hello); System.out.println("rmi server is ready ..."); } catch (RemoteException | MalformedURLException e) { e.printStackTrace(); } } //不再通过rmiregistry单独运行 通过编程来实现 private static void test2(){ try { Hello hello = new HelloImpl(); //注册服务 Registry registry = LocateRegistry.createRegistry(1099); registry.rebind("user", hello); System.out.println("rmi server is ready ..."); } catch (RemoteException e ) { e.printStackTrace(); } } }
客户端调用(建议另外启一个项目,这样模拟更真实)
先写一个相同的接口,接口的全限量名要和服务端的保持一致
package com.rmi; import java.rmi.Remote; import java.rmi.RemoteException; /** * @Description: 远程调用 * @Author: xiaoaa * @Date: 2020/12/12 */ public interface Hello extends Remote { public String sayHello(String name) throws RemoteException; } 测试代码 ```java package com.rmi; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; public class RmiTest { public static void main(String[] args) { test2(); } private static void test1(){ try { Hello hello = (Hello) Naming.lookup("user"); System.out.println(hello.sayHello("xiao")); } 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(); } } private static void test2(){ try { Hello hello = (Hello) Naming.lookup("rmi://127.0.0.1:1099/user"); System.out.println(hello.sayHello("xiao")); } 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(); } } }
写得不好的地方,请见谅。大家有踩到坑的也欢迎分享,谢谢。
-
RMI 远程调用
2019-04-21 01:18:34NULL 博文链接:https://laodaobazi.iteye.com/blog/1917439 -
Rmi远程调用
2017-08-30 15:46:561.创建远程接口RemoteService,继承(extends)java.rmi.Remote,里面的抽象方法抛出java.rmi.RemoteException 2.创建实现类RemoteServiceImpl,继承java.rmi.server.UnicastRemoteObject,实现RemoteService 3.指定...不想再各种查一遍了,写个总结记录
编写被调用的程序
1.创建远程接口RemoteService,继承(extends)java.rmi.Remote,里面的抽象方法抛出java.rmi.RemoteException
2.创建实现类RemoteServiceImpl,继承java.rmi.server.UnicastRemoteObject,实现RemoteService
3.指定通信端口的类,本地调用可省略,远程调用需要指定
import java.rmi.server.*; import java.io.*; import java.net.*; public class RMISocket extends RMISocketFactory { public Socket createSocket(String host, int port) throws IOException{ return new Socket(host,port); } public ServerSocket createServerSocket(int port) throws IOException { if (port==0) port = 'XXXX'; return new ServerSocket(port); } }
XXXX为指定的通信端口号
4.注册程序的类Start.class,代码可直接写在main方法里:
try {
RMISocketFactory.setSocketFactory(new RMISocket());//本地调用可省略 RemoteService service=new RemoteServiceImpl(); Context namingContext=new InitialContext(); namingContext.rebind("rmi://xxx.xx.x.xxx:1099/name", service); } catch (NamingException e) { e.printStackTrace(); }
注册被调用的程序xxx.xx.x.xxx为ip地址,name为注册名称,可自定义,此方法也要抛出java.rmi.RemoteException
打开cmd
1.切换到程序的bin目录下
2.rmic cn.aaa.bbb.RemoteServiceImpl,完成后会在bin目录下生成一个RemoteServiceImpl_Stub.class文件
3.在bin目录下创建mypolicy.policy,内容
grant {
permission java.security.AllPermission "", ""; //"", ""的内容可根据需要自定义
};
4.start rmiregistry(linux下是rmiregistery)
5.java -Djava.security.policy=mypolicy -Djava.ext.dirs=..\lib cn.aaa.bbb.Start..\lib是被调用程序需要引用的jar目录,没有就不用写
打包被调用的程序
1.eclipse里直接右键export-jar file,其他默认下一步即可
2.将生成的jar file用压缩软件打开,将RemoteServiceImpl_Stub.class文件添加到RemoteServiceImpl同一目录下
编写调用的程序
1.将打包好的jar file添加到程序目录,并build path
2.调用的代码
注:远程调用,被调用的服务器需要打开端口1099和XXXXtry{ RemoteService service=(RemoteService)Naming.lookup("rmi://xxx.xx.x.xxx:1099/name"); result=service.method(param); }catch(Exception e){ e.printStackTrace(); }
-
rmi远程调用
2014-04-30 10:34:101. Java远程方法调用(RMI)提供了Java程序语言的远程通讯功能, 这种特性使客户机上运行的程序可以调用远程服务器上的... 在 Java 中,远程对象是实现远程接口的类的实例, 远程接口声明每个要远程调用的方法。 在1. Java远程方法调用(RMI)提供了Java程序语言的远程通讯功能,
这种特性使客户机上运行的程序可以调用远程服务器上的对象,
使Java编程人员能够在网络环境中分布操作。2.创建一个简单的Java分布式远程方法调用程序可以按以下几个步骤操作,
一、定义远程接口:
package my.rmi; import java.rmi.Remote; import java.rmi.RemoteException; /** * 在 Java 中,远程对象是实现远程接口的类的实例, 远程接口声明每个要远程调用的方法。 * 在需要创建一个远程对象的时候,我们通过传递一个接口来隐藏基层的实施细节,客户通过接口句柄发送消息即可。 * 远程接口具有如下特点: * 1) 远程接口必须为public属性。如果不这样,除非客户端与远程接口在同一个包内, * 否则 当试图装入实现该远程接口的远程对象时,调用会得到错误结果。 * 2) 远程接口必须扩展接口java.rmi.Remote。 * 3) 除与应用程序本身特定的例外之外,远程接口中的每个方法都必须在自己的throws从句中 * 声明java.rmi.RemoteException。(或 RemoteException 的父类)。 * 4) 作为参数或返回值传递的一个远程对象(不管是直接,还是本地对象中嵌入)必须声明为远 程接口, * 而不应声明为实施类。 * @author Wang 2014年4月30日 * */ public interface MyRmiService extends Remote{ /** * 加法 * @param x * @param y */ public Integer addition(Integer x,Integer y)throws RemoteException; /** * 减法 * @param x * @param y */ public Integer subtraction(Integer x,Integer y)throws RemoteException; /** * 乘法 * @param x * @param y */ public Integer multiplication(Integer x,Integer y)throws RemoteException; /** * 除法 * @param x * @param y */ public Integer division(Integer x,Integer y)throws RemoteException; }
二、实现远程接口:
package my.rmi; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; /** * 远程对象实现类必须扩展远程对象java.rmi.UnicastRemoteObject类,并实现所定义的远程接口。 * 远程对象的实现类中包含实现每个远程接口所指定的远程方法的代码。这个类也可以含有附加的方法, * 但客户只能使用远程接口中的方法。因为客户是指向接口的一个句柄,而不是它的哪个类。必须为远程对象 * 定义构造函数,即使只准备定义一个默认构造函数,用它调用基础类构造函数。因为基础类构造函数可能会 * 抛出 java.rmi.RemoteException,所以即使别无它用必须抛出java.rmi.RemoteException例外。 * @author Wang 2014年4月30日 * */ public class MyRmiServiceImpl extends UnicastRemoteObject implements MyRmiService{ private static final long serialVersionUID = 1L; protected MyRmiServiceImpl() throws RemoteException { super(); } @Override public Integer addition(Integer x, Integer y) throws RemoteException { return x+y; } @Override public Integer subtraction(Integer x, Integer y) throws RemoteException { return x-y; } @Override public Integer multiplication(Integer x, Integer y) throws RemoteException { return x*y; } @Override public Integer division(Integer x, Integer y) throws RemoteException { if(y!=0){ return x/y; }else{ return 0; } } }
三、编写服务器类:package my.rmi; import java.rmi.Naming; import java.rmi.registry.LocateRegistry; /** * 包含 main 方法的类可以是实现类自身,也可以完全是另一个类。 * 下面通过RmiSampleServer 来创建一个远程对象的实例,并通过java.rmi.registry.LocateRegistry类 * 的createRegistry 方法从指定端口号启动注册服务程序,也可以通过执行 rmiregistry 命令启动注册服 * 务程序,注册服务程序的缺省运行端口为 1099。必须将远程对象名字绑定到对远程对象的引用上: * @author Wang 2014年4月30日 */ public class MyRmiServer { public static void main(String[] args) { try { MyRmiService rmiService=new MyRmiServiceImpl(); //注册通讯端口 LocateRegistry.createRegistry(6600); /** * 注册通讯路径 * 6600端口不能被其他进程占用,MyRmiService貌似可以随意,最好用你的service类名。 */ Naming.rebind("rmi://127.0.0.1:6600/MyRmiService", rmiService); System.out.println("Service Start!"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
四、编写使用远程服务的客户机类
客户机类的主要功能有两个,一是通过Naming.lookup方法来构造注册服务程序 stub 程序实例,
二是调用服务器远程对象上的远程方法。package my.rmi; import java.rmi.Naming; import java.rmi.RemoteException; public class MyRmiClient { public static void main(String[] args) { add(1,2,1); } public static void add(int a,int b,int c){ //如果c==1,执行执行本地程序,否则远程调用程序。 if(c==1){ try { MyRmiService rmiService = new MyRmiServiceImpl(); System.out.println(" 1 + 2 = " + rmiService.addition(1, 2)); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ try { /** * IP为你的代理机的IP地址,即如果在本机运行的远程代理服务,则为 * 你的本机IP,如果在虚拟机运行的远程代理服务,则为你的虚拟机IP。 */ String url = "rmi://172.16.12.215:6600/MyRmiService"; MyRmiService rmiService = (MyRmiService)Naming.lookup(url); System.out.println(" 1 + 2 = " + rmiService.addition(1, 2)); } catch (RemoteException rex) { System.out.println("Error in lookup: " + rex.toString()); } catch (java.net.MalformedURLException me) { System.out.println("Malformed URL: " + me.toString()); } catch (java.rmi.NotBoundException ne) { System.out.println("NotBound: " + ne.toString()); } } } }
3.现在所有代码都已经完成,然后导出jar文件,main入口指向MyRmiServer的main方法。可以在本机或虚拟机上运行jar文件。
命令为:java -jar rmi.jar
然后执行MyRmiClient的方法测试远程调用是否成功。
-
RMI远程调用时的内外网端口映射问题(RMI远程调用如何穿透防火墙)
2015-08-22 11:44:14以下是一个RMI项目的实施笔记: 环境:防火墙(XX卫士) 外网IP:x.x.x.135 port:8400/8500 内网IP: x.x.x.90 port:8400/8500 内外网IP及port互相映射....1.RMI远程调用的服务注册到:内网的8400端以下是一个RMI项目的实施笔记:
环境:防火墙(XX卫士) 外网IP:x.x.x.135 port:8400/8500 内网IP: x.x.x.90 port:8400/8500
内外网IP及port互相映射. (外网IP是设定的防火墙上的,管理员进行映射配置)
1.RMI远程调用的服务注册到:内网的8400端口上 (不用创建stub/skeleton,不使用缺省的1099)
//LocateRegistry.createRegistry(rmiServerPort); //定义服务注册与查找服务端口 8400
2.RMI远程调用的数据通信端口绑定到:内网的8500端口上
// RMISocketFactory.setSocketFactory(new SMRMISocket()); //定义数据传输端口 8500
3.设定访问外网:x.x.x.135:8400时穿透防火墙访问内网:x.x.x.90:8400
//在sun.rmi.server.UnicastRef调用时的服务IP为:x.x.x.135,而不是x.x.x.90(客户的本地不存在RMI服务嘛)
//System.setProperty("java.rmi.server.hostname","x.x.x.135");
4.RMI调用穿透防火墙不是自动的,需要我们手工指定,就如3所指,如果不加设定,在互联网上就不能通过外网IP访问内网IP上绑定的RMI服务
5.通过本项目,明白了RMI可以不用rmic创建stub/skeleton的,也可以不用启动rmiRegistry而通过程序直接启动RMI服务,这样方便了代码的修改与项目的实施.
由于是项目代码,所以不打包上传代码了。简写如下:
///RMI服务端代码
String hostIP = InetAddress.getLocalHost().getHostAddress();
int rmiServerPort = 8400; //数据传输服务为8400
String bindName = "licenseServer";
//下面这行代码不能少,否则当路由器x.x.x.135映射到的内网IP:x.x.x.90时,
//访问RMI服务时将导向本地的x.x.x.90,那么客户端就是访问本地x.x.x.90,
//这绝对错误.服务是在公网路由器(含公共IP)的后面,不在客户的本地
System.setProperty("java.rmi.server.hostname","x.x.x.135");
try {
RMISocketFactory.setSocketFactory(new SMRMISocket()); //定义数据传输端口 8500
LocateRegistry.createRegistry(rmiServerPort); //定义服务注册与查找服务端口 8400
}
catch (Exception ex) {
System.out.println("服务器端口绑定时发生错误:"+ex.getMessage());
ex.printStackTrace();
}
//创建license生成器的服务对象
MakeLicense licenseService = new MakeLicenseService();
//绑定一个服务对象到一个服务端口
//URL format (without the scheme component)
String bindUrl = "//"+hostIP+":"+ rmiServerPort +"/"+bindName;
Naming.rebind(bindUrl, licenseService);
System.out.println(bindUrl+" server is ready.");SMRMISocket.java
import java.rmi.server.*;
import java.io.*;
import java.net.*;public class SMRMISocket
extends RMISocketFactory {public Socket createSocket(String host, int port) throws IOException {
return new Socket(host, port);
}public ServerSocket createServerSocket(int port) throws IOException {
if (port == 0)
port = 8500;System.out.println("RMI服务器的注册与数据传输端口 ="+port);
return new ServerSocket(port);
}}
客户端代码:
private String remoteHost = "x.x.x.135"; //公网IP或局域网IP
private int rmiServerPort=8400; //查找服务端口 8400
private String bindName = "licenseServer"; //RMI服务名称
private int revCount = 0;
private MakeLicense remoteObject=null;
public LicenseRequestClient() {
try{
// if(System.getSecurityManager()==null){
// System.setSecurityManager(new RMISecurityManager());
// }
String bindUrl = "//"+remoteHost+":"+ rmiServerPort +"/"+bindName;
System.out.println("请求的远程服务URL="+bindUrl);
MakeLicense remoteObject = (MakeLicense) Naming.lookup(bindUrl);
this.remoteObject = remoteObject;
// System.out.print("远程remoteObject="+remoteObject);
}
catch (RemoteException re) {
System.out.println("RemoteException:" + re);
}
catch (NotBoundException nbe) {
System.out.println("NotBoundException:" + nbe);
}
catch (MalformedURLException mfe) {
System.out.println("MalformedURLException:" + mfe);
}
} -
【Java基础】网络编程-RMI远程调用
2020-01-30 21:09:42文章目录RMI远程调用基本概念Java实现RMI远程调用Java实现RMI远程调用2Java的RMI远程调用弊端小结 RMI远程调用基本概念 Java的RMI远程调用是指,一个JVM中的代码可以通过网络实现远程调用另一个JVM的某个方法。RMI... -
java RMI 远程调用
2019-09-25 11:13:33在学习代理模式的过程中接触到了远程调用,jdk有自己的RMI实现,所以这边自己实现了RMI远程调用,并记录下心得. 感受最深的是RMI和现在的微服务有点相似,都是通过"注册中心"来获取数据,比如spring cloud 中通过feign来... -
RMI远程调用实验
2020-07-17 11:23:19RMI远程调用实验目录步骤及说明 目录 步骤及说明 1、设计一个学生成绩管理系统,利用RMI、注册表以及JNDI实现客户端对服务器对象的远程访问。 2、建立Java工程RMIServer,创建包com.rmiDB,新建类DBmanager,在... -
java rmi 远程调用实现
2017-10-06 21:20:55Java RMI 远程调用实例 -
rmi远程调用读取文件
2012-04-05 17:40:16rmi远程调用读取文件,以及各种简单的例子 -
聊一聊网络编程中RMI远程调用
2021-01-19 09:29:07原文链接聊一聊网络编程中RMI远程调用 1.网络编程 网络编程是开发中经常需要用到的一个点,也是面试中必考题,本节对RMI远程调用做一下简单的演示。 ❝以下知识点均来源于廖雪峰官方网站... -
JAVA RMI远程调用
2016-08-06 22:15:41JAVA RMI远程调用:http://haolloyin.blog.51cto.com/1177454/332426/ JAVA RMI Spring配置:http://ithead.iteye.com/blog/1465299 -
JAVA RMI远程调用实现
2019-02-22 20:00:32JAVA RMI远程调用实现 #创建RMI Server 1.创建Java实体类,实现序列化 package com.hp.beans; import java.io.Serializable; public class user implements Serializable { private Integer uid; private ... -
一个小demo:RMI 远程调用
2019-08-13 21:58:12一个小demo:RMI 远程调用
-
交互设计那些事儿(二)
-
Codesys V3系列软PLC编程(IEC61131-3)入门学习笔记V1.0.1.pdf
-
MySQL 查询与高级查询(多表、嵌套和正则表达式)
-
时间整形飞秒激光诱导熔融硅表面纳米周期条纹的电子动力学研究
-
DaoEasyEditorpro-090312-全容错.mse
-
表实体字段忽略非数据库字段的注解
-
云开发后台+微信扫码点餐小程序+cms网页管理后台 含后厨端和用户端
-
flow-db-admin:与流路由器一起使用的Meteor数据库管理软件包-源码
-
蔬菜-源码
-
Amoeba 实现 MySQL 高可用、负载均衡和读写分离
-
表空间使用情况及新增表空间文件
-
vue.xmind ,vue入门思维导图
-
linux基础入门和项目实战部署系列课程
-
基于曲率特征混合分类的高密度点云降噪方法
-
基于3D封装的TSV建模和热分析
-
C/C++反汇编解密
-
2021年 系统架构设计师 系列课
-
多级菜单 递归 Java
-
jss-theme-reactor:不可维护强大的主题层,可与JS库中的jss CSS一起使用-源码
-
libFuzzer视频教程