RMI执行过程

RMI

是Java编程语言里一种用于实现远程过程调用的应用程序编程接口。它使客户机上的运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能地简化远程接口对象的使用,简而言之就是为了调用远程方法。https://blog.csdn.net/u012291108/article/details/52863915

这个过程中涉及到对象的传输,所以会用到序列化和反序列化,以及JNDI(远程方法调用协议)

参考文章:

rmi详解:https://blog.csdn.net/weixin_44627989/article/details/93055791

stub(存根)和skeleton(框架):https://www.cnblogs.com/yin-jingyu/archive/2012/06/14/2549361.html

现在我们的需求是这样的:client想执行一个在远程机器上server的一个方法,如果我们手动去编写这些过程,socket网络编程或许是我们必须要面对的,由此便引入了stub和skeleton模型。

所有和网络相关的代码全部都由这两个部分来实现,这样一来,客户端和服务端就都不需要去处理网络相关的代码,,这样的逻辑结构

在这里插入图片描述

简单来说就是这样的一个逻辑

Client<->Stub<->socket<->skeleton<->server

在远程服务开启的时候,stub也没有办法知道server的域名和端口,这个时候就要用到RMIRegistry,server上会创建一个stub对象,然后把他注册到RMIRegistry,这样Client就能从RMIRegistry获取到对象,RMIRegistry会在1099端口导出自己,不设置的话默认是1099端口

调试代码

server端代码

server端接口

package test;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloService extends Remote {
    public String sayHello() throws RemoteException;
    //要发布的服务类的方法必须都throws RemoteException
    //在Util中,创建代理对象时会checkMethod,存在没有throws RemoteException的则抛出IllegalArgumentException
}

server端实现代码

package test;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
    //需要继承UnicastRemoteObject,因为Remote只是接口,UnicastRemoteObject是Remote的实现类
//还要实现HelloService这个接口

    protected HelloServiceImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello() throws RemoteException{
        return "hello";
    }

}

server端将端口开放出去

package test;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

//服务器端
public class Server {
    final static String host = "127.0.0.1";
    final static int port = 8080;
    public static void main(String[] args) throws RemoteException, MalformedURLException {
        HelloService helloService = new HelloServiceImpl();
        Registry reg=LocateRegistry.createRegistry(port);

        reg.rebind("rmi://"+host+":"+port+"/hello", helloService);//不写port默认是1099
        System.out.println("服务启动...");
    }

}

客户端代码

package test;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class Client {

    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        HelloService helloService = (HelloService) Naming.lookup("rmi://127.0.0.1:8080/hello");
        //默认端口1099
        System.out.println(helloService.sayHello());
    }

}

跟踪服务端启动执行流程

在HelloServoceImpl里面实现父类无参构造

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mRo5wyTc-1626770660728)(D:\typora\picture\image-20210630141440888.png)]

进入UnicastRemoteObject方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ty91YKS3-1626770660729)(D:\typora\picture\image-20210630141428214.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BRnsPcPH-1626770660730)(D:\typora\picture\image-20210630141549404.png)]

步入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pPClwYaF-1626770660732)(D:\typora\picture\image-20210630145656142.png)]

步入UnicastServerRef()函数,其中会new一个UnicastServerRef对象,进入UnicastServerRef类,会发现其父类中包含一个LiveRef类型的属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sLbYqtjF-1626770660733)(D:\typora\picture\image-20210630145845070.png)]

步入这个liveRef函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vYMGW447-1626770660733)(D:\typora\picture\image-20210630150044758.png)]

TCPEndpoint对象就获得了Ip以及端口的属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5z7dtKk-1626770660733)(D:\typora\picture\image-20210630150346546.png)]

紧接着步入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REfjAOrD-1626770660734)(D:\typora\picture\image-20210630150558956.png)]

这时会判断obj是否为UnicastRemoteObject类型,由于obj为自定义的HelloServiceImpl,继承了UnicastRemoteObject,因此if条件成立,接着步入sref对象的exportObject函数
在这里插入图片描述

可以看到会先获得对象的类属性,然后创建一个代理对象,继续查看源码,可以知道该代理对象类型为HelloServiceImpl,handler为RemoteObjectInvocationHandler,其中handler会包括上面创建的LiveRef对象(前面也知道了该对象中包含Endpoint等通信所需信息),因此可以判断在远程调用该对象时,客户端获取到的其实是该代理对象,再往下看,会生成一个Target对象,可以看到该Target对象包含了许多数据(导出输入的原始对象,创建的代理对象等)

后面这个target对象又被this.ref的exportObject方法导出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwcFcvNE-1626770660735)(D:\typora\picture\image-20210630150848233.png)]

步入ref.exportObject方法,此时就是在TCPEndpoint这个类里面,在transport这个里面是有这些属性的,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hy3RkaHH-1626770660736)(D:\typora\picture\image-20210630151131232.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5slPTHVO-1626770660736)(D:\typora\picture\image-20210630151451197.png)]

跟进这个exportObject()函数,可以看到这个Listen函数,就是建立监听

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2OvEDqYG-1626770660737)(D:\typora\picture\image-20210630164524158.png)]

接着跟进,在listen函数里面首先获得TCPEndpoint对象

然后获取到端口

后面就会开启一个socket,而且这个端口是随机的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DE41uc6V-1626770660737)(D:\typora\picture\image-20210630165314334.png)]

继承这个父类构造函数的作用

UnicastRemoteObject基类的构造方法将远程对象发布到一个随机端口上,当然端口也可以指定

每一个对象都会有一个单独的socket,端口值随机

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1AFucT7t-1626770660737)(D:\typora\picture\image-20210630173018594.png)]

可以看到在执行完构造HelloServiceImpl的构造的时候,这个对象属性里面就有了ip和端口的信息

执行下一行代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NLs27r0t-1626770660738)(D:\typora\picture\image-20210630172252918.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jb0qcXK4-1626770660738)(D:\typora\picture\image-20210630165729405.png)]

步入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-txfIVUtI-1626770660739)(D:\typora\picture\image-20210630170043972.png)]

步入setup函数,这里会执行exportObject()方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YBLvmShq-1626770660740)(D:\typora\picture\image-20210630170242690.png)]

跟进这个方法,这个方法就会返回stub存根,生成skeleton对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YHxjAx8G-1626770660740)(D:\typora\picture\image-20210630170350594.png)]

总结

Registry reg=LocateRegistry.createRegistry(port);

reg.rebind("rmi://"+host+":"+port+"/hello", helloService);

上面这两行代码就是建立Registry对象,然后将对象和请求放进这个hashTable中去,当我们请求

rmi://127.0.0.1:8080/hello   

就会对应的访问到这个对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xumCZHuX-1626770660740)(D:\typora\picture\image-20210630173217457.png)]

总结:


1、首先接口要继承Remote这个类,然后实现类要继承UnicastRemoteObject这个类,通过这个类的构造方法将远程对象发布到一个随机端口上

2、然后新建Registry对象,这个对象的作用有两个

​ ①在指定端口设置外部访问

​ ②相当于建立了一个注册表,通过键和值来进行访问指定对象的方法

这就是整体建立客户端的一个逻辑

客户端执行流程跟踪

客户端的这行代码返回的是RegistryImpl_Stub,也就是一个存根

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DnINQFmX-1626770660741)(D:\typora\picture\image-20210701110611242.png)]

传入域名和端口获取到服务端的RegistryImpl的代理

下周仔细研究一下Java RMI Registry各个版本安全问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值