RMI原理揭秘之远程对象
2016-06-27 21:51
387 查看
讨论开始之前,我们先看看网上的一个例子,这个例子我腾抄了一分,没有用链接的方式,只是为了让大家看得方便,如有侵权,我立马***。
定义远程接口:
3. 定义实现类:
4.服务端:
5.客户端:
6.输出结果:
1)服务端输出:
Remote Hello Object is bound sucessfully!
Hello!
Hello!
Hello!
Hello!
Hello!
2)客户端输出:
0
1
2
3
4
7.把实现类更改为不继续承UnicastRemoteObject
8.输出结果:
1)服务端输出:
Remote Hello Object is bound sucessfully!
2)客户端输出:
Hello!
1
Hello!
1
Hello!
1
Hello!
1
Hello!
1
这两个用例的输出结果来看,前一个用例index计数器一直在增加,且Hello!输出在服务端。这说明
helloWorld方法执行是在服务端,客户端的每一次对象方法调用,都是对服务端对象的调用。
而后一个用例就不同了。helloWorld方法执行是在客户端,且每次lookup出来的都是新的对象。
我们看一下lookup出来的对象类型:
实现类继承UnicastRemoteObject时,lookup出来的对象类型是$Proxy0,而不继承UnicastRemoteObject时类,对象类型是com.guojje.Hello。
我们把继承UnicastRemoteObject类的对象叫做远程对象,我们lookup出来的对象,只是该远程对象的存根(Stub)对象,而远程对象永远在远方。客户端每一次的方法调用,最后都是仅有的那一个远程对象的方法调用。
没有继UnicastRemoteObject类的对象,同样可以bind到Registry,但lookup出来了对象却是远程对象
经过序列化,然后到客户端反序列化出来的新的对象,后续的方法调用与远程对象再无关系。
那UnicastRemoteObject类的继承是如何影响这些的呢? 我们来探索一下。
仅仅new一个远程对象,运行这个程序,我们就发现进程不会退出。它hang在哪儿呢?
jstack查看线程栈,发现有SocketAccept在监听:
的确启动了一个监听端口。ServerSocket类上添加debug.得到调用栈如下:
UnicastRemoteObject基类的构造方法将远程对象发布到一个随机端口上,当然端口也可以指定。
迎来一个重要的方法(UnicastServerRef.java):
这里的stub变量就是我们lookup出来的远程对象的存根对象。而target保留了远程对象的信息集合:
对象,存根,objId等。
接着看TCPTransport.java的exportObject方法:
listen()启动监听。这里对TcpTransport加了同步,防止多个线程同时执行,同时也防止同一端口启动多次。
一次TcpTransport会有一个监听端口,而一个端口上可能会发部多个远程对象。exportCount计录该TcpTransport发布了多少个对象。
ObjectTable为静态方法调用,那么所有的远程对象信息(target)都会放到这个对象表中。我们这个target包含了远程对象几乎所有的信息,找到他,就找到远程对象的存根对象。
远程对象的发布,先说这么多。我们再看一下lookup是如何找到远程对象的存根的.
当然先从远程对象的bind说起:
RegistryImpl.java:
bindings里放得确实是远程对象本身,而不是他的存根。这是怎么回事?继续追究lookup方法。
RegistryImpl_Skel类是rmic生成所有没有源代码,我们只能从反编译的代码中查看一点信息:
从网络流中读取第一个参数必须是lookup的字符串参数。然后调用服务端RegistryImpl对象lookup
方法,该方法返回的的确是远程对象,而非远程对象的存根呀?
问题的原因在于下面的序列化过程,继续看调用栈:
看一个重要方法MarshalOutputStream.java
如果对象是java.rmi.Remote实例,则向对象表中找到该对象的Target,如果存在,则返回其存根对象。
所以返回服务端的变成了远程对象的存根。先到此,后续再探索其方法调用,安全等相关问题。
补充:
其实Registry的实现类RegistryImpl也是个远程对象,这里registry.bind却没有进行远程调用。这是因为我是用LocateRegistry.createRegistry(8888)创建的远程对象,而不是通过LocateRegistry.getRegistry(8888)获取的远程对象,而getRegistry返回的是RegistryImpl的Stub.仅此而已。
定义远程接口:
1)服务端输出:
Remote Hello Object is bound sucessfully!
Hello!
Hello!
Hello!
Hello!
Hello!
2)客户端输出:
0
1
2
3
4
7.把实现类更改为不继续承UnicastRemoteObject
1)服务端输出:
Remote Hello Object is bound sucessfully!
2)客户端输出:
Hello!
1
Hello!
1
Hello!
1
Hello!
1
Hello!
1
这两个用例的输出结果来看,前一个用例index计数器一直在增加,且Hello!输出在服务端。这说明
helloWorld方法执行是在服务端,客户端的每一次对象方法调用,都是对服务端对象的调用。
而后一个用例就不同了。helloWorld方法执行是在客户端,且每次lookup出来的都是新的对象。
我们看一下lookup出来的对象类型:
我们把继承UnicastRemoteObject类的对象叫做远程对象,我们lookup出来的对象,只是该远程对象的存根(Stub)对象,而远程对象永远在远方。客户端每一次的方法调用,最后都是仅有的那一个远程对象的方法调用。
没有继UnicastRemoteObject类的对象,同样可以bind到Registry,但lookup出来了对象却是远程对象
经过序列化,然后到客户端反序列化出来的新的对象,后续的方法调用与远程对象再无关系。
那UnicastRemoteObject类的继承是如何影响这些的呢? 我们来探索一下。
jstack查看线程栈,发现有SocketAccept在监听:
的确启动了一个监听端口。ServerSocket类上添加debug.得到调用栈如下:
UnicastRemoteObject基类的构造方法将远程对象发布到一个随机端口上,当然端口也可以指定。
对象,存根,objId等。
接着看TCPTransport.java的exportObject方法:
一次TcpTransport会有一个监听端口,而一个端口上可能会发部多个远程对象。exportCount计录该TcpTransport发布了多少个对象。
远程对象的发布,先说这么多。我们再看一下lookup是如何找到远程对象的存根的.
当然先从远程对象的bind说起:
RegistryImpl.java:
RegistryImpl_Skel类是rmic生成所有没有源代码,我们只能从反编译的代码中查看一点信息:
方法,该方法返回的的确是远程对象,而非远程对象的存根呀?
问题的原因在于下面的序列化过程,继续看调用栈:
看一个重要方法MarshalOutputStream.java
所以返回服务端的变成了远程对象的存根。先到此,后续再探索其方法调用,安全等相关问题。
补充:
其实Registry的实现类RegistryImpl也是个远程对象,这里registry.bind却没有进行远程调用。这是因为我是用LocateRegistry.createRegistry(8888)创建的远程对象,而不是通过LocateRegistry.getRegistry(8888)获取的远程对象,而getRegistry返回的是RegistryImpl的Stub.仅此而已。
相关文章推荐
- 黑马程序员-------- java语法基础知识
- Android TextView标点符号换行问题
- 使用SmartUpload上传文件时getparameter获取不到值
- 黑马程序员 ——————关于switch语句的学习
- Tomcat conf目录下web.xml,tomcat-users.xml,server.xml以及context.xml的作用
- android adb 下at执行
- struts2-查看网站源码
- HTML基础
- JAVA JDBC 调用存储过程 传参数
- node.js(2)
- windows平台下安装linux虚拟机,宿主机与虚拟机ping不通,提示connect network is unreachable
- github desktop使用方法
- android 中的线性布局与相对布局
- 加载数字图片(cocos2dx)
- 考前复习——排序模板
- 最近邻(kNN,k-NearestNeighbor), 贝叶斯(Bayes), BPNN 人工神经网络(BPNN)算法之花的分类
- 使用Hadoop提取网络日志KPI指标
- 网络渗透概述
- http://www.linuxidc.com/Linux/2016-04/129738.htm
- 浅谈TCP/IP协议四层构成的主要功能