rmi入门

来源:互联网 发布:淘宝网安全套 编辑:程序博客网 时间:2024/06/06 01:02

先来一个最简单的例子:


1.业务接口类

package Simple;import java.rmi.Remote;import java.rmi.RemoteException;public interface Business extends Remote{String doBusiness(String businessCode) throws RemoteException;}

2.业务实现类

package Simple;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class BusinessImpl extends UnicastRemoteObject implements Business{protected BusinessImpl() throws RemoteException {super();// TODO Auto-generated constructor stub}/** *  */private static final long serialVersionUID = 1L;@Overridepublic String doBusiness(String businessCode) throws RemoteException {// TODO Auto-generated method stubreturn businessCode + " has been done!";}}

3.Server类

package Simple;import java.rmi.Naming;import java.rmi.registry.LocateRegistry;public class SimpleServer {public static void main(String[] args)throws Exception{Business busi = new BusinessImpl();LocateRegistry.createRegistry(1097);Naming.rebind("rmi://localhost:1097/busi",busi);}}

4.Client类

package Simple;import java.rmi.Naming;public class Client {public static void main(String[] args)throws Exception{Business busi = (Business)Naming.lookup("rmi://localhost:1097/busi");System.out.println(busi.doBusiness("001"));}}

备注说明:

1.从JDK5.0开始,rmi编程不再需要编程人员借助rmic生成stub等辅助类,也不需要手动启动Register。也就是说这些工作JDK已经帮你做好啦

2.EJB的SessionBean其实就是rmi技术和JNDI技术的结合,上述的例子是把业务对象(或stub)存储在Register中,而EJB则把业务对象(或stub)存储在JDNI空间里.

3.LocateRegistry.createRegistry(1097);这里的1097(默认为1099)我们称之为通讯端口或者查找端口,服务端在createRegistry时实际上会new ServerSocket(1097),客户端的socket)通过与端口号为1097的服务端端口互联lookup到busi对象(实际上是stub,为方便描述,下文不再特别指出)。客户端获取到的busi(stub)对象在和服务端的sketon对象进行通讯时实际上也会建立socket连接,数据传输时的ServerSocket也需要一个端口(不同于通讯端口),我们称为之数据端口。这个ServerSocket是在BusinessImpl的父类UnicastRemoteObject的构造方法中初始化的,默认调用new ServerSocket(0),即随便选择一个端口通讯。但在生产环境中,防火墙往往限制通讯端口,只允许部分端口开放。所以上述程序可能会无法穿透防火墙,这时候我们可以强行绑定数据端口.


package Simple;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.rmi.Naming;import java.rmi.registry.LocateRegistry;import java.rmi.server.RMISocketFactory;public class Server{public static void main(String... args) throws Exception{RMISocketFactory.setSocketFactory(new RMISocketFactory(){@Overridepublic ServerSocket createServerSocket(int port) throws IOException {if(port == 0)port = 14800;return new ServerSocket(port);}@Overridepublic Socket createSocket(String host, int port)throws IOException {return new Socket(host,port);}});LocateRegistry.createRegistry(1097);Business busi = new BusinessImpl();Naming.rebind("rmi://localhost:1097/busi",busi);}}

我们通过RMISocketFactory.setSocketFactory来控制绑定生成ServerSocket的端口号.在这里LocateRegistry.createRegistry时会调用匿名内部类的createServerSocket生成new ServerSocket(1097),在new BusinessImpl()时会调用匿名内部类的createServerSocket生成new ServerSocket(14800)(原来port=0进行了重绑定),同时也会调用匿名内部类的createSocket生成new Socket("localhost",1097)。在这里一定要注意new BusinessImpl()在setSocketFactory之后才会绑定成功。

我们也可以把LocateRegistry.createRegistry是生成ServerSocket的策略和new BusinessImpl()生成ServerSocket和Socket的策略区别开来,如下:

package Simple;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.rmi.Naming;import java.rmi.registry.LocateRegistry;import java.rmi.server.RMIClientSocketFactory;import java.rmi.server.RMIServerSocketFactory;import java.rmi.server.RMISocketFactory;public class Server{public static void main(String... args) throws Exception{RMISocketFactory.setSocketFactory(new RMISocketFactory(){@Overridepublic ServerSocket createServerSocket(int port) throws IOException {if(port == 0)--没有作用了(肯定==0)port = 14800;return new ServerSocket(port);}@Overridepublic Socket createSocket(String host, int port)throws IOException {return new Socket(host,port);}});LocateRegistry.createRegistry(1097,new RMIClientSocketFactory(){@Overridepublic Socket createSocket(String host, int port)throws IOException {return new Socket(host,port);}},new RMIServerSocketFactory(){@Overridepublic ServerSocket createServerSocket(int port) throws IOException {return new ServerSocket(port);}});Business busi = new BusinessImpl();Naming.rebind("rmi://localhost:1097/busi",busi);}}

显然LocateRegistry.createRegistry会使用参数RMIClientSocketFactory的createServerSocket去new ServerSocket(1097).

而new BusinessImpl()还会使用RMISocketFactory.setSocketFactory参数中相关方式,new ServerSocket(34800)和new Socket("localhost",1907).这时候方法中if(port==0)的判断也没有意义了(肯定==0了,LocateRegistry.createRegistry也不会使用这里的工厂生成serverSocket(1907)了)。


再来一种更简单的实现(需要对BusinessImpl进行一点改进)

package Simple;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class BusinessImpl extends UnicastRemoteObject implements Business{protected BusinessImpl() throws RemoteException {super();}protected BusinessImpl(int port) throws RemoteException{super(port);}/** *  */private static final long serialVersionUID = 1L;@Overridepublic String doBusiness(String businessCode) throws RemoteException {// TODO Auto-generated method stubreturn businessCode + " has been done!";}}
package Simple;import java.rmi.Naming;import java.rmi.registry.LocateRegistry;public class Server{public static void main(String... args) throws Exception{LocateRegistry.createRegistry(1097);Business busi = new BusinessImpl(34880);Naming.rebind("rmi://localhost:1097/busi",busi);}}

够简单的吧,哈哈。


最后再来谈谈默认的套接字工厂吧。

默认的套接字工厂实现通过三层方法来创建客户机套接字。首先,尝试进行到远程 VM 的直接套接字连接。如果该操作失败(因防火墙的问题),则运行时使用具有服务器显式端口号的 HTTP。如果防火墙不允许此类型的通信,则服务器上的 cgi-bin 脚本的 HTTP 用于 POST(发送)该 RMI 调用。
sun.rmi.transport.proxy.RMIMasterSocketFactory失败后,会使用另外两个类sun.rmi.transport.proxy.RMIHttpToPortSocketFactory和sun.rmi.transport.proxy.RMIHttpToCGISocketFactory来实现通过HTTP协议,不过这个需要服务器端配合。在jdk文档下有一个例子docs\technotes\guides\rmi\archives里面有Servlet实现。


参与文献:

(1)http://www.huomo.cn/developer/article-1b147.html 让JAVA的RMI彻底穿透防火墙

(2)http://hi.baidu.com/netjava/item/c2efd413ce50d2fa9c778a1c RMI穿透防火墙

(3)http://skanion.iteye.com/blog/1168747 RMI心得(注册端口)

(4)http://blog.csdn.net/ktyl2000/article/details/4485896 RMI远程调用时的内外网端口映射问题(RMI远程调用如何穿透防火墙)

(5)http://www.blogjava.net/china-qd/archive/2006/04/25/42927.html 用RMI进行远程方法调用

(6)http://blog.csdn.net/sun_boyhappy/article/details/5655014 RMI IP绑定和端口固定

(7)http://bbs.csdn.net/topics/220029980 RMISocketFactory的含义和RMI的理解

(8)java_RMI技术讲解.doc

(9)RMI开发权威指南.pdf