rmi入门

先来一个最简单的例子:


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;

	@Override
	public String doBusiness(String businessCode) throws RemoteException {
		// TODO Auto-generated method stub
		return 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(){
			@Override
			public ServerSocket createServerSocket(int port) throws IOException {
				if(port == 0)
					port = 14800;
				return new ServerSocket(port);
			}
			@Override
			public 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(){
			@Override
			public ServerSocket createServerSocket(int port) throws IOException {
				if(port == 0)--没有作用了(肯定==0)
					port = 14800;
				return new ServerSocket(port);
			}
			@Override
			public Socket createSocket(String host, int port)
					throws IOException {
				return new Socket(host,port);
			}
			
		});
		LocateRegistry.createRegistry(1097,new RMIClientSocketFactory(){

			@Override
			public Socket createSocket(String host, int port)
					throws IOException {
				return new Socket(host,port);
			}},new RMIServerSocketFactory(){

			@Override
			public 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;

	@Override
	public String doBusiness(String businessCode) throws RemoteException {
		// TODO Auto-generated method stub
		return 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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值