说实话我每次看到RMI的流程我都觉得脑袋疼。而且分析完了都不记得分析了个什么鸟。
或许这次会好一点。
RMI远程类调用
RMI的作用就是客户端调用服务端的远程类。
我们开两个项目,一个做客户端,一个做服务端。用jdk8u65
RMIServer
定义一个继承了Remote的接口RemoteInterface
importjava.rmi.Remote;
importjava.rmi.RemoteException;
publicinterfaceRemoteInterfaceextendsRemote{
publicStringsayHello(Stringname) throwsRemoteException;
}
一个实现该接口的类RemoteImpl,需要继承UnicastRemoteObject
importjava.rmi.RemoteException;
importjava.rmi.server.UnicastRemoteObject;
publicclassRemoteImplextendsUnicastRemoteObjectimplementsRemoteInterface{
protectedRemoteImpl() throwsRemoteException{
}
publicStringsayHello(Stringname) {
System.out.println("Hello "+name);
return"Hello "+name;
}
}
RemoteImpl必须加入一个调用了父类构造函数的构造函数。
这里的无参构造函数实际上会自动向第一行插入一条隐式的
super()
,如下。而不写构造函数是自动生成无参构造函数,不满足调用super(args...);
的要求,所以报错。protectedRemoteImpl() throwsRemoteException{
super();
}
向外开放服务的RMIServer。需要LocateRegistry.createRegistry(1099),开放注册中心到1099端口。并把类绑定到注册中心。
importjava.rmi.AlreadyBoundException;
importjava.rmi.RemoteException;
importjava.rmi.registry.LocateRegistry;
importjava.rmi.registry.Registry;
publicclassRMIServer{
publicstaticvoidmain(String[] args) throwsRemoteException, AlreadyBoundException{
RemoteInterfaceremoteImpl=newRemoteImpl();
Registryregistry=LocateRegistry.createRegistry(1099);
registry.bind("remoteImpl", remoteImpl);
}
}
RMIClient
需要一个相同的接口RemoteInterface
importjava.rmi.Remote;
importjava.rmi.RemoteException;
publicinterfaceRemoteInterfaceextendsRemote{
publicStringsayHello(Stringname) throwsRemoteException;
}
使用服务端远程类的,客户端RMIClient
importjava.rmi.NotBoundException;
importjava.rmi.RemoteException;
importjava.rmi.registry.LocateRegistry;
importjava.rmi.registry.Registry;
publicclassRMIClient{
publicstaticvoidmain(String[] args) throwsRemoteException, NotBoundException{
Registryregistry=LocateRegistry.getRegistry("127.0.0.1", 1099);
RemoteInterfaceremoteImpl=(RemoteInterface) registry.lookup("remoteImpl");
System.out.println(remoteImpl.sayHello("RMI"));
}
}
先开服务端,再开客户端。客户端就能调用到服务端上的方法sayHello
源码分析
移步视频:
https://www.bilibili.com/video/BV1L3411a7ax?p=1&vd_source=732f44595cd3e361ab78ff559f3c5ab5
此处概述+只给利用点和利用方式,因为分析了也记不住。我直接偷偷分析,挂上来我自己都不看。
偷个包浆RMI通信老图
先从服务端开始
服务端
RemoteInterface remoteImpl = new RemoteImpl();
从UnicastServerRef一路跟到了TCPEndpoint.getLocalEndpoint,获取了IP
在UnicastServerRef.exportObject完成了创建远程对象的主要逻辑,前面都是层层封装,没什么好看的。
生成了一个stub并赋值,这是服务端stub
实际上,赋值调用的Util.creatProxy()内部,就是生成了一个代理类。等于说stub就是个代理类,如下
handler来自RemoteObjectInvocationHandler,看到该类满足代理handler的要求,继承了InvocationHandler
还记得动态代理吗,调用代理类的方法会自动走进handler的invoke方法(虽然这里没调用,但是为后面你使用sayHello的时候做了铺垫)
这里RemoteObjectInvocationHandler.invoke就是加了个判断的普通invoke执行方法
下面一堆调用,从ref.exportObject跟到TCPTransport.exportObject。listen()方法开始监听。跟进去看看
从ep中提取了Endpoint(翻译为终端)和端口。ep就是之前TCPEndpoint.getLocalEndpoint生成的。然后调用了newServerSocket
在newServerSocket中,createServerSocket新建了一个Websocket。并且监听端口为0,会调用setDefaultPort获取端口。从前面的调试信息也可以看到,一路过来端口都是默认的0
setDefaultPort内部,获取了所有的本地未开放端口,并异步循环取最后一个端口。就是随机取一个端口啦
从这里开始,port就有值了。
继续运行到UnicastServerRef.exportObject。在get不到值的时候会向map里put键值。
虽然是put进去了,但是只有个类名。方法名还需要跳转到UnicastServerRef$HashToMethod_Maps.computeValue中获取
从接口中循环获取方法名,设为可访问,put进map中
最后我们生成的remoteImpl对象,有LiveRef包含的host和一个随机的端口号,还有装填了方法的hashToMethod_Map。这些都封装在UnicastServerRef的一个对象中。创建了一个代理类stub,但是return时并没有存储,只是写了个逻辑在这。
但是服务端开随机的端口号,客户端怎么知道这个端口号来获取类呢?下一步就是解决这个问题
Registry registry = LocateRegistry.createRegistry(1099);
新建了一个指定端口为1099空的LiveRef对象
在随后的setup函数中,同样的来到UnicastServerRef.exportObject完成代理对象stub的创建。这是注册端stub
注意,这里创建stub时,跟进Util.creatProxy->stubClassExists()。
如果存在remoteClass_Stub类(在这里就是RegistryImpl_Stub类),返回true
可以看到该类是存在的
在creatStub创建了RegistryImpl_Stub类
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容