server
先编写一个接口,必须继承Remote,定义一个方法,这个方法就是远程被调用的方法接口
1 2 3 4 5 6 7 8
| package org.javasec.RMI.rmi1;
import java.rmi.Remote; import java.rmi.RemoteException;
public interface test extends Remote { void printest(String str) throws RemoteException; }
|
再编写一个接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package org.javasec.RMI.rmi1; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject;
public class testimpl extends UnicastRemoteObject implements test { public testimpl() throws RemoteException { super(); } public void printest(String str) { System.out.println("远程方法-调用成功"); System.out.println("你好 "+str); } }
|
必须调用父类的构造方法,实现定义的接口
然后开始编写服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package org.javasec.RMI.rmi1;
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry;
public class RmiServer { public static void main(String[] args) throws RemoteException, MalformedURLException { LocateRegistry.createRegistry(1099); System.out.println("RMI注册表-启动成功"); test t1= new testimpl(); Naming.rebind("rmi://127.0.0.1:1099/test",t1); System.out.println("Test服务注册启动-成功"); System.out.println("Test服务运行中......"); } }
|
在一个端口,启动注册表,然后实例化远程对象,把服务命名绑定到对象上启动服务
Client
lookup根据命名查找远程服务方法,然后直接像本地那样调用就行
1 2 3 4 5 6 7 8 9 10 11 12
| package org.javasec.RMI.rmi1;
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException;
public class RmiClient { public static void main(String[] args) throws java.rmi.RemoteException, MalformedURLException, NotBoundException { test t1=(test) Naming.lookup("rmi://127.0.0.1:1099/test"); t1.printest("X1ly?S"); } }
|

理解通信和结构
rmi有三个核心组件
1 2 3 4 5
| ┌─────────────┐ 查找 ┌─────────────┐ 注册 ┌─────────────┐ │ RMI Client│ ────────> │ RMI Registry│ <──────── │ RMI Server │ │ │ │ │ │ │ │ 调用远程方法 │ │ 作为命名服务 │ │ 提供具体实现 │ └─────────────┘ └─────────────┘ └─────────────┘
|
整个RMI通信过程会建立两次TCP连接,为什么呢?

- 第一步-连接Registry,根据命名查找远程服务(第一次TCP握手)
1
| test t1=(test) Naming.lookup("rmi://127.0.0.1:9090/test");
|
这里client发起TCP握手,连接Registry 9090端口,这个不是server仅仅是注册表,提供了服务命名和具体对象的映射管理,本身不执行远程方法
在 ReturnData 消息中包含序列化数据
这个端口是动态分配的,真正的远程方法的端口,用于实际的远程方法调用。
- 第三步-建立实际的方法调用连接(第二次 TCP 握手)
这里才真正连接到 33769 端口进行方法调用
Registry 只做一件事:维护名称到对象的映射关系,它不执行实际的远程方法。