RMI执行过程分析

客户端代码

public class RMIClient {
    public static void main(String... args) throws RemoteException, NotBoundException, MalformedURLException {
        if (args == null || args.length <= 1) {
            System.out.println("usage : java -jar RMIClient.jar rmi_server_ip content");
            System.exit(0);
        }
        IRMIService service = (IRMIService) Naming.lookup("rmi://"+args[0]+":1099/RMIServer");
        System.out.println(service.speakToYourself(args[1]));
        //speakToYourself 这个方法仅仅就是在输入参数前面加上了另外一个字符串而已,然后返回。
    }
}

抓包结果

上述代码执行过程中,使用wireshark进行抓包,得到如下结果
这里写图片描述

抓包结果分析:
48-49 :tcp的三次握手
54-69 :通信数据
70-72 :断开连接

54-69中协议类型凡是tcp的,都是确认其他数据包的确认包,比如55号:
这里写图片描述

其他为rmi协议的才包含通信数据。

矛盾

oracle文档中介绍rmi的背景时说:
截图
这里写图片描述

 socket要求client和server参与到应用层协议,以便对交换的信息进行编码和解码。这种协议的制定就是笨重的和容易出错的。
 官方连接:https://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-intro2.html

但是跟踪rmi源码可以知道,rmi同样时通过socket来通信的。所以这让人感觉有点矛盾。。

源码截图

这里写图片描述

Connection封装

通过socket拿到Connection后,rmi对conn进行了tcpconnection的封装

这里写图片描述

写入rmi协议头部信息

这里写图片描述

对照抓取到的包信息:

这里写图片描述

两者是符合的。

原理分析

rmi底层采用了Stub 和 Skeletons机制。。
Skeletons:运行在server端,负责分发请求。接到client端的请求后,会做三件事:

  1. unmarshals 客户端发送来的数据
  2. 根据收到的数据,执行相关的方法,拿到执行结果
  3. marshals执行结果,发送给client

Stub:运行在client端,需要调用远程方法时,会做下面几件事(基本和skeleton反着来)

  1. marshals待发送数据并发送
  2. 等待结果
  3. unmarshals收到的数据

个人认为那个skeletons就有点类似于spring的dispatcherservlet,当收到客户端请求后,负责把请求分发到相应的controller进行处理。

针对上面给出的客户端代码,客户端发送的数据就是 :

"rmi://"+args[0]+":1099/RMIServer"

接收到的结果就是:

IRMIService service  //这样一个实例

注意

拿到 IRMIService service 这样一个实例后,当调用service的 service.speakToYourself(args[1])方法时,并没有与服务器通信。
也就是说这个方法的执行是在本地执行,而非在远程服务器上执行后再回传结果
注意,是 本地执行

那么rmi说的远程调用,怎么体现远程呢?
这个远程调用指的是,客户端发送”rmi://”+args[0]+”:1099/RMIServer”到获取service实例的过程,这个是在远程服务器上执行的。

本质

跟踪代码执行过程可以知道,从客户端发送数据,到拿到service实例的过程,其实就是
对象的序列号–>网络传输–>反序列化 的过程

上面提到的marshal 和 unmarshal的意思,其实就是 serialize和 unserialize的意思。这个可以查证wiki:https://en.wikipedia.org/wiki/Marshalling_(computer_science) ,在代码上也的确是这么回事。

底层采用的IO模型

查看源码可以知道,现在的rmi实现采用的io是bio,并没有采用jdk1.4提供的nio功能。看截图:
这里写图片描述

这里写图片描述

这里写图片描述

PS:我使用的jdk是1.8版本的

JDK8版本及以前版本,RMI采用的IO模型是 BIO

其他

补充阅读:https://www.ietf.org/rfc/rfc2713.txt rmi协议rfc文档

查看tcp报文段首部的格式,发现并没有一个标示来标示tcp上层采用的是什么协议,所以凡是需要用到在tcp协议之上的协议时,都需要自己来处理获取到的数据,自己根据需要按照某种协议格式进行处理数据。
(PS:IP数据报首部是有这样的8bit的空间来标示上一层协议是什么协议)

也就是说当我们拿到通过tcp层传上来的数据时,需要我们自己按照某种协议的格式去处理数据

好比上面rmi协议,可以发现发送数据的时候,是程序自己完成写rmi首部字段工作的。

所以使用http协议时,也需要自己完成 写首部,收到数据后分析处理首部等工作。既然如此,可以验证下tomcat在处理请求的时候,是不是这样子处理收到的数据和发送数据的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值