学习资料:Java安全漫谈—RMI篇

RMI的工作原理

RMI的全称:Remote Method Invocation,远程方法台调用,目的是实现某个Java虚拟机上的对象调用另一个Java虚拟机中对象的方法。

  1. package RMI;
  2. import java.rmi.Naming;
  3. import java.rmi.Remote;
  4. import java.rmi.RemoteException;
  5. import java.rmi.registry.LocateRegistry;
  6. import java.rmi.server.UnicastRemoteObject;
  7. public class RMIServer {
  8. public interface IRemoteHelloWorld extends Remote{
  9. public String hello() throws RemoteException;
  10. }
  11. public class RemoteHelloWorld extends UnicastRemoteObject implements IRemoteHelloWorld {
  12. protected RemoteHelloWorld() throws RemoteException{
  13. super();//调用父类的hello()方法
  14. }
  15. @Override//重写父类的方法
  16. public String hello() throws RemoteException {
  17. System.out.println("call from");
  18. return "Hello world";
  19. }
  20. }
  21. private void start() throws Exception{
  22. RemoteHelloWorld h = new RemoteHelloWorld();
  23. LocateRegistry.createRegistry(1099);
  24. Naming.rebind("rmi://127.0.0.1:1099/Hello", h);
  25. }
  26. public static void main(String[] args) throws Exception {
  27. new RMIServer().start();
  28. }
  29. }

前面是定义一个父类,定义一个子类,父类中的构造方法不写任何内容,而是选择在子类重写父类的构造方法。方法写完后,开始创建一个RMI实例,将这个实例绑定到一个地址上,也就是127.0.0.1:1099/Hello上,此时Server已经准备好了。

然后是客户端代码

  1. package RMI;
  2. import java.rmi.Naming;
  3. public class TrainMain {
  4. public static void main(String[] args) throws Exception{
  5. RMIServer.IRemoteHelloWorld hello = (RMIServer.IRemoteHelloWorld) Naming.lookup("rmi://192.168.136.1:1099/Hello");
  6. String ret = hello.hello();
  7. System.out.println(ret);
  8. }
  9. }

代码很明确,使用Naming.lookupRegister中查找名字为Hello的对象,然后调用远程方法,就像是调用本地的一样。

wireshark抓包的操作就不做了,大概看了之后理解了(实际上是自己抓了之后数据混乱不方便看)有兴趣的还是看P牛的Java安全漫谈。

主要流程是:服务端在127.0.0.1:1099这里注册了一个名为Hello的对象,当客户端请求Register去查找Hello这个Name时,服务端给它返回了一个序列化的数据,返回到客户端进行反序列化,得到的结果是一个新的地址端口,192.168.136.1:xxxx可能是随机的端口,然后客户端再去请求这个新的端口,最后成功调用了远程方法。文字描述可能不太明显,这里做了一个图,方便以后再看时快速回忆。

🌭RMI原理和实现流程 - 图1

现在就明白了,RMI Register不会执行远程方法,但是服务端在上面注册了一个Name绑定对象,当客户端通过Name来查找这个对象的时候,就可以知道这个对象具体在哪里,然后访问真实的地址进行调用就可以了。

个人理解为一个图书馆管理员,你问她要一本书,她会告诉你书在哪,让你自己去找。

然后根据Java安全漫谈的RMI篇(二)跟进学习

首先是关于RMI的实现原理

  1. LocateRegistry.createRegistry(1099);
  2. Naming.bind("rmi://127.0.0.1:1099/Hello", new RemoteHelloWprld());

第一行创建并运行RMI Register

第二行将RemoteHelloWorld对象绑定到Hello这个名字上

Naming.bind的第一个参数应该是一个url,内容包括host主机地址和端口号,以及远程对象的名字。形式应该是http://host:port/name

还一种方式

  1. Naming.bind("Hello", new RemoteHelloWorld());

这句代码在写的时候省略了一些内容,就是这个urlhostport,默认是localhost1099

bind()方法负责把指定名称绑定给远程对象,rebind()方法负责把指定名称重新绑定到一个新的远程对象。如果那个名称已经绑定过了,先前的绑定会被替换掉。