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来通信的。所以这让人感觉有点矛盾。。
通过socket拿到Connection后,rmi对conn进行了tcpconnection的封装
对照抓取到的包信息:
两者是符合的。
rmi底层采用了Stub 和 Skeletons机制。。
Skeletons:运行在server端,负责分发请求。接到client端的请求后,会做三件事:
Stub:运行在client端,需要调用远程方法时,会做下面几件事(基本和skeleton反着来)
个人认为那个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) ,在代码上也的确是这么回事。
补充阅读:https://www.ietf.org/rfc/rfc2713.txt rmi协议rfc文档
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/wilsonpeng3/article/details/47286145