一个正常工作的RMI系统由下面几个部分组成:
1、远程服务的接口定义
2、远程服务接口的具体实现
3、桩(Stub)和框架(Skeleton)文件
4、一个运行远程服务的服务器
5、一个RMI命名服务,它允许客户端去发现这个远程服务
6、类文件的提供者(一个HTTP或者FTP服务器)
7、一个需要这个远程服务的客户端程序
二、RMI(远程方法调用)的工作原理
RMI系统结构,在客户端和服务器端都有几层结构。
--------- ----------
| 客户 | | 服务器|
---------- ----------
| |
------------- ----------
| 占位程序 | | 骨干网 |
-------------- -----------
| |
------------------------------------
| 远 程 引 用 层 |
------------------------------------
| |
------------------------------------
| 传 输 层 |
------------------------------------
方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传 输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。 占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追追踪可以接受方法调用的远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序获得返回值。
步骤1:定义rmi服务接口,需要从Remote接口扩展,注意其中的方法必须抛出RemoteException异常,否则无法绑定:
package rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface NameService extends Remote{
/**
* 必须抛出RemoteException异常,否则无法绑定
* @return
* @throws RemoteException
*/
String getName()throws RemoteException;
}
package rmi;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
/**
* 一般从UnicastRemoteObject继承, 所有可以被远程调用的对象都必须扩展该类,让客户机与服务器对象实例建立一对一的连接
* @author yandong_yu
*
*/
public class NameServiceimpl implements NameService{
/**
* 必须定义构造方法,因为要抛出RemoteException异常
* @throws RemoteException
*/
protected NameServiceimpl() throws RemoteException {
super();
}
/**
*
*/
private static final long serialVersionUID = 6049452763111963360L;
@Override
public String getName()throws RemoteException {
return "yuyd";
}
}
步骤3:注册类:
public static void main(String[] args) throws RemoteException, MalformedURLException, AlreadyBoundException, NotBoundException{
String ip = "localhost";
int port = 8889;
/**
* 创建该端口对应的注册对象,该注册对象内部有一个hashTable,维护了ServiceName到服务对象的对应关系
*/
Registry reg=LocateRegistry.createRegistry(port);
NameServiceimpl service = new NameServiceimpl();
/**
* 绑定一个ip,端口和服务名,该方法会在Registry上面创建一个nameservice为key值的服务对象
*/
Naming.rebind("//" + ip + ":" + port + "/nameservice", service);
System.out.println("register rmi successful");
}
步骤4:客户端调用:
public static void main(String[] args) throws RemoteException, MalformedURLException, AlreadyBoundException, NotBoundException{
String ip = "localhost";
int port = 8889;
/**
* clientService是一种远端实现
*/
NameService clientService = (NameService)Naming.lookup("//" + ip + ":" + port + "/nameservice");
if(clientService!=null){
System.out.println("lookup rmi successful");
System.out.println(clientService.getName());
}
}