-- 包 --
java.rmi 包
Remote接口
- 介绍: Rmi的一个核心接口
- 作用: 用于标识 远程接口, 被标识的接口的方法可以从非本地JVM调用
- 使用(标识)方法: 将要想标识的接口 继承Remote接口, 同时其所有方法都要抛出
RemoteException
, 这样该接口定义的方法就具备了非本地JVM调用的能力, 成为远程接口
Naming类
- 作用: 提供了更简便的 在远程对象注册表中存储和获取 远程对象的引用 的方法
效果与使用Registry接口的方法相同, 用Registry绑定在 名称name上的 远程对象 也可通过Naming方法来操作, 反之同理
注:以下name的格式皆为 //host:port/name 指定了主机地址 端口号 名称name
-- 静态方法 --
Remote lookup(String name)
作用:返回 URL格式名称name绑定的远程引用(存根)
void bind(String name, Remote obj)
作用:将远程引用obj(通常是存根)绑定到 URL格式名称name中
void unbind(String name)
作用:删除 URL格式名称name的绑定
void rebind(String name, Remote obj)
作用:将URL格式名称name的绑定 替换为远程引用 obj(通常是存根)
String[] list(String name)
作用:返回URL格式名称name(格式中没有name部分)中存在绑定的名称的数组
注:对非本地主机的注册表使用bind unbind rebind三种方法会报错
obj:远程引用obj只能是远程类的实例,同时也代表着 若多次远程调用远程对象时 操控的是同一个对象
RemoteException异常类
- 介绍: 在执行远程方法调用期间可能发生的许多与通信相关的异常的公共超类, 远程接口的每个方法, 即扩展
java.rmi.Remote
的接口,必须在其 throws 子句中列出 RemoteException
java.rmi.registry 包
Registry接口
- 作用: 用于实现远程对象注册表的远程接口
- 父接口: Remote
-- 实例方法 --
Remote lookup(String name)
作用:返回此注册表中 名称name绑定的远程引用(存根)
void bind(String name, Remote obj)
作用:将远程引用obj(通常是存根)绑定到 此注册表中的名称name中
void unbind(String name)
作用:删除此注册表中 名称name的绑定
void rebind(String name, Remote obj)
作用:将此注册表中的名称name的绑定 替换为远程引用 obj(通常是存根)
String[] list()
作用:返回在此注册表中存在绑定的名称的数组
注:对非本地主机的注册表使用bind unbind rebind三种方法会报错
obj:远程引用obj只能是远程类的实例,同时也代表着 若多次远程调用远程对象时 操控的是同一个对象
LocateRegistry类
- 作用: 获取对特定主机(包括本地主机)上远程对象注册表的引用, 和创建本机远程对象注册表
-- 静态方法 --
Registry createRegistry(int port)
作用:在本地主机创建注册表 并返回,该注册表接收port端口的请求
Registry createRegistry(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
作用:在本地主机创建注册表 并返回,该注册表接收port端口的请求(可以自定义套接字工厂)
//csf:客户端套接字工厂,用于创建连接到 注册表 的客户端套接字
//ssf:服务端套接字工厂,用于创建接受与 注册表 连接的服务端套接字
Registry getRegistry()
作用:返回本地主机1099端口上的 远程对象注册表的引用(存根)
Registry getRegistry(int port)
作用:返回本地主机port端口上的 远程对象注册表的引用(存根)
Registry getRegistry(String host)
作用:返回host指定主机1099端口上的 远程对象注册表的引用(存根)
Registry getRegistry(String host, int port)
作用:返回host指定主机port端口上的 远程对象注册表的引用(存根)
Registry getRegistry(String host, int port, RMIClientSocketFactory csf)
作用:返回host指定主机port端口上的 远程对象注册表的引用 (可以自定义客户端套接字工厂)
java.rmi.server包
UnicastRemoteObject类
- 作用: 用于创建和管理远程对象的关键类, 给继承该类的对象赋予远程调用的能力, 负责将对象导出到RMI运行时系统(存根), 使其能够接收来自客户端的方法调用, 成为远程类
- 使用方法: 远程类继承该类(同时要实现远程接口), 并在构造函数中调用UnicastRemoteObject类无参数构造函数
- 父类: RemoteServer
-- 过程 --
介绍
- RPC: Remote Procedure Call 远程过程调用, 即要像调用本地的函数一样去调远程函数, 它并不是某一个具体的框架,而是实现了远程过程调用的都可以称之为RPC, RMI就是一个实现了RPC的框架
- 概念: RMI是Java中的一种分布式对象技术, 它允许一个JVM中的对象调用另外一个JVM中对象的方法, 使得在不同的 Java 程序之间进行远程通信和对象交互成为可能
- 存根: 存根就像是远程对象在本地的副本, 客户端代码可以像调用本地对象的方法一样调用存根对象的方法, 然而, 存根的主要职责并非真正执行方法逻辑, 而是负责将客户端的方法调用请求封装并发送到远程服务器上的实际对象, 同时接收服务器返回的结果并传递给客户端
- 流程:
- 服务端注册RMI服务, 绑定上远程对象obj, 注册在指定的 端口 或 url
- 客户端通过 ip端口 或 url 访问对应RMI服务, 并查找其绑定的obj对象
- 服务端返回 obj对象 所在地址与端口
- 客户端访问 该地址与端口
- 服务端根据 客户端的操作 操作该 obj 对象, 并返回结果的序列化数据
- 客户端反序列化数据得到结果
- 注: RMI Registry就像一个网关,他自自己是不会执行远程方法的,但RMI Server可以在上⾯面注册⼀一个Name 到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMI Server;最后,远程⽅方法实际上在RMI Server上调⽤用
远程接口
- 特点: 必须要继承
java.rmi.Remote
, 且接口内的每个方法都要抛出 RemoteException
import java.rmi.Remote;
import java.rmi.RemoteException;
//必须要继承java.rmi.Remote
public interface MyRemoteInterface extends Remote {
//每个方法都要抛出 RemoteException
public String sayHello() throws RemoteException;
}
远程接口实现类
- 特点: 必须继承
java.rmi.server.UnicastRemoteObject
, 以允许JVM创建远程的存根/代理
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class MyRemoteObject extends UnicastRemoteObject implements MyRemoteInterface {
@Override
public String sayHello() throws RemoteException {
return "Hello, Remote World!";
}
}
注册表
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.Naming;
//使用registry.bind注册
public class RMIServer {
public static void main(String[] args) {
//实例化一个实现类
MyRemoteObject obj = new MyRemoteObject();
//将RMI服务注册端口设置为1099端口
Registry registry = LocateRegistry.createRegistry(1099); // 默认端口是 1099
//注册RMI服务,服务名为"MyRemoteObject"
registry.bind("MyRemoteObject", obj);
}
}
//使用Naming.bind注册
public class RMIServer {
public static void main(String[] args) {
//实例化一个实现类
MyRemoteObject obj = new MyRemoteObject();
//将RMI服务注册端口设置为1099端口
LocateRegistry.createRegistry(1099); // 默认端口是 1099
//注册RMI服务,服务url为rmi://127.0.0.1:1099/MyRemoteObject,并绑定对象obj
Naming.bind("rmi://127.0.0.1:1099/MyRemoteObject", obj);
}
}
客户端
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.Naming;
import org.vulhub.RMI.RMIServer;
//使用registry.bind注册的服务
public class RMIClient {
public static void main(String[] args) {
//连接到服务器ip对应的服务器的1099端口
Registry registry = LocateRegistry.getRegistry("服务器ip", 1099);
//查找名称为"MyRemoteObject"的服务绑定的对象 并强制转型为MyRemoteInterface接口
MyRemoteInterface remoteObj = (MyRemoteInterface) registry.lookup("MyRemoteObject");
//列出 服务器ip 绑定的所有对象 并强制转型为MyRemoteInterface接口数组
MyRemoteInterface[] remoteObj = (MyRemoteInterface[]) registry.list();
//调用接口方法
String message = remoteObj.sayHello();
//打印接口方法的调用结果
System.out.println(message);
}
}
//使用Naming.bind注册的服务
public class RMIClient {
public static void main(String[] args) {
//查找 rmi://服务器ip:1099/MyRemoteObject 对应服务所绑定的对象 并强制转型为MyRemoteInterface接口
MyRemoteInterface remoteObj = (MyRemoteInterface) Naming.lookup("rmi://服务器ip:1099/MyRemoteObject");
//查找 rmi://服务器ip:1099/ 绑定的所有对象 并强制转型为MyRemoteInterface接口数组
MyRemoteInterface[] remoteObj = (MyRemoteInterface[]) Naming.list("rmi://服务器ip:1099/");
//调用接口方法
String message = remoteObj.sayHello();
//打印接口方法的调用结果
System.out.println(message);
}
}