先介绍一下RMI。RMI:远程方法调用(Remote Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法。
RMI 只支持使用java。
端口:RMI会有两个端口,一个默认的1099端口,另一个是随机分配的端口,用来传输数据。这个随机端口在穿透防火墙或者访问linux的时候就会出现访问拒绝的情况。我在连接阿里云的服务器的时候,由于没有意识到这个问题,所以一直connect refused。后来才知道是这个随机端口的原因。把随机端口固定后,在阿里云中配置安全组规则,才可以访问。
服务器代码如下:
1 SMRMISocket类用来固定随机端口
public class SMRMISocket extends RMISocketFactory {
public Socket createSocket(String host, int port) throws IOException {
return new Socket(host,port);
}
@Override
public ServerSocket createServerSocket(int port) throws IOException{
if (port == 0){
port = 8500;
}
System.out.println("rmi服务器的注册与数据传输端口="+port);
return new ServerSocket(port);
}
}
2 编写接口以及接口的实现
Remote 接口是一个标识接口,用于标识所包含的方法可以从非本地虚拟机上调用的接口
接口实现类
public interface ImageProcessor extends UnicastRemoteObject implements ImageProcessor {
* 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常
*
* @throws RemoteException
*/
public ImageProcesserImpl() throws RemoteException{
}
@Override
public void test(){
System.out.println("test");
}
}
3编写Server
public class RmiServer {
public static void main(String[] args) {
try {
System.setProperty("java.rmi.server.hostname","xx.xx.x.xx"); //如果要连接公网的话需要写这一句话来指定要访问的地址。不然客户端访问的时候获得的代理引用的地址是服务器的内网地址,是连不上的
RMISocketFactory.setSocketFactory(new SMRMISocket()); //定于数据传输端口8500
ImageProcessor imageProcessor = ImageProcesserImpl.class.newInstance();
Registry registry = LocateRegistry.createRegistry(8888); //定义服务注册与查找服务端口8400
registry.rebind("imageProcessor", imageProcessor); //客户端会根据字符串来查找绑定的对象
System.out.println("bind success");
}catch (Exception e){
e.printStackTrace();
}
}
客户端代码如下:
接口:与Server端的接口一摸一样
Client 即程序的入口
public static void main(String[] args ){
ImageProcessor rhello =(ImageProcessor) Naming.lookup(
"rmi://"xx.xx.x.x:xxxx
/imageProcessor"
); //需要获得的代理对象强转成你需要的类型
rhello.test(); //代理对象及存根 Stub 调用test()方法会连接Server端的test()的实现方法
}
注意:1连接远程的时候要在Server中指定java.rmi.server.hostname
2连接远程的时候要在Server中将随机端口固定,需要的话要配置安全组规则。我一直连接不上的原因就是没有意识到固定随机端口并配置安全组规则导致的。
3 Server启动后可以使用telnet ip 端口来测试能不能连通
4 在Client中,可以使用String[] strArray = Naming.list("rmi://xx.xx.xx.xx:xxxx")来获取服务器绑定的所有的名字。
5 客户端和服务器端的文件需要在相同路径的包下面,如果不在一个项目,包的路径及名字也应该相同(必须)。
6 如果传输对象类型的数据的话,需要将对象序列化才可以。
这是我初次使用RMI。上面的代码也有手打的。如果有问题或者补充,欢迎留言。