JDK文档阅读之Using Custom Socket Factories with Java RMI

来源:互联网 发布:济南网络推广 编辑:程序博客网 时间:2024/06/06 00:01

1.自定义的Socket工厂可以用来做什么?

控制远程方法调用在网络级别的通信方式。例如:

  • 设置Socket选项
  • 控制地址绑定
  • 控制连接的建立(例如要求身份验证)
  • 控制数据编码(例如添加加密和压缩)

客户端socket工厂:

  • 控制产生sockets,用来初始化远程调用。
  • 控制连接如何建立,使用哪种类型的socket。

服务器端socket工厂:

  • 控制生成服务器端sockets,用来接收远程调用
  • 控制传入的连接是如何被监听和接受的,以及用于输入连接socket的类型。

2.建立自定义的Socket工厂

  • 实现自定义ServerSocket和Socket
    在JDK8文档实例中,自定义socket工厂创建sockets,使用了XOR加密和解密技术。而这种技术可以被知识渊博的密码分析员解密,JDK8文档使用仅仅是为了Demo的简单性。XOR sockets 使用特别的输入输出流实现来处理从socket中写入和输出数据。

(具体的代码请自行下载jdk8文档查阅)

class XorSocket extends Socket {}class XorInputStream extends FilterInputStream {}class XorOutputStream extends FilterOutputStream {}class XorServerSocket extends ServerSocket {}
  • 实现自定义RMIClientSocketFactory
    客户端socket工厂,XorClientSocketFactory
    • 实现了java.rmi.server.RMIClientSocketFactory接口,需要实现createSocket方法以用来返回适当的客户端socket实例,一个XorSocket。
    • 实现java.io.Serializable接口以实现实例可以序列化客户端作为远程存根(存根就相当于一个代理)的一部分。实现equals和hashcode方法可以正确地在等价的客户端socket工厂的远程存根实例之间分享资源。
import java.io.IOException;import java.io.Serializable;import java.net.Socket;import java.rmi.server.RMIClientSocketFactory;/** * @author liuffei * @date 2017年9月18日 * @description */public class XorClientSocketFactory implements RMIClientSocketFactory, Serializable {    private byte pattern;    public XorClientSocketFactory(byte pattern){        this.pattern = pattern;    }    @Override    public Socket createSocket(String host, int port) throws IOException {        return new XorSocket(host,port,pattern);    }    public int hashCode(){        return pattern;    }    public boolean equals(Object obj){        return (getClass() == obj.getClass() && pattern == ((XorClientSocketFactory)obj).pattern);    }}
  • 实现自定义RMIServerSocketFactory
    服务器端socket工厂,XorServerSocketFactory,实现了java.rmi.server.RMIServerSocketFactory接口。服务器端socket工厂需要实现createServerSocket方法来返回适当的服务器socket实例,一个XorServerSocket。
    • 服务器端socket工厂不需要实现Serializable接口,因为服务器socket工厂实例没有包含在远程存根中。
    • 同样需要实现equals和hashcode方法,JAVA RMI 实现可以正确地在等价的socket工厂导出的远程对象之间分享资源。
import java.io.IOException;import java.net.ServerSocket;import java.rmi.server.RMIServerSocketFactory;public class XorServerSocketFactory implements RMIServerSocketFactory {    private byte pattern;    public XorServerSocketFactory(byte pattern){        this.pattern = pattern;    }    @Override    public ServerSocket createServerSocket(int port) throws IOException {        return new XorServerSocket(port,pattern);    }    public int hashCode(){        return pattern;    }    public boolean equals(Object obj){        return (getClass() == obj.getClass() && pattern == ((XorServerSocketFactory)obj).pattern);    }}

3.在应用中使用自定义的Socket工厂

两个步骤:

  • 服务器应用:创建远程对象并且导出。存储一个远程对象存根的引用在Java RMI注册表,这样客户端才能查找到它。
  • 查找远程对象存根和调用远程方法。客户端应用不需要引用自定义socket工厂。客户端为远程对象寻找存根时,客户端socket工厂将会被下载到客户端。

第一步:服务器应用
如果远程对象通信要求使用自定义的sockets,需要指定当你导出远程对象时使用哪一个自定义socket工厂。当你的应用导出指定的自定义socket工厂,Java RMI运行时会使用相应的自定义RMIServerSocketFactory来创建服务器socket,以接受远程对象的远程调用,并且会创建一个相应的自定义RMIClientSocketFactory存根。当使用存根对远程对象进行远程调用时,将使用客户端socket工厂创建连接。

import java.rmi.Remote;import java.rmi.RemoteException;public interface Hello extends Remote{    String sayHello() throws RemoteException;}
import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.rmi.server.RMIClientSocketFactory;import java.rmi.server.RMIServerSocketFactory;import java.rmi.server.UnicastRemoteObject;public class HelloImpl implements Hello {    public HelloImpl(){}    public String sayHello(){        return "Hello World!!!";    }    public static void main(String args[]){        if(System.getSecurityManager() == null){            System.setSecurityManager(new SecurityManager());        }        byte pattern = (byte)0xAC;        try{                                HelloImpl obj = new HelloImpl();            RMIClientSocketFactory csf = new XorClientSocketFactory(pattern);            RMIServerSocketFactory ssf = new XorServerSocketFactory(pattern);            Hello stub = (Hello) UnicastRemoteObject.exportObject(obj,0,csf,ssf);            LocateRegistry.createRegistry(2002);            Registry registry = LocateRegistry.getRegistry(2002);            registry.rebind("Hello",stub);            System.out.println("HelloImpl bound in registry");        }catch(Exception e){            System.out.println("HelloImpl exception:"+e.getMessage());            e.printStackTrace();        }    }}

Server应用创建的远程对象实现了远程接口Hello,并且使用自定义的socket工厂导出对象,使用java.rmi.server.UnicastRemoteObject的exportObject方法,自定义的socket工厂作为参数。接着,创建本地注册表,在注册表里,为一个名称为Hello的远程对象的存根绑定引用。

第二步:客户端应用

import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;public class HelloClient {    public static void main(String args[]){        if(System.getSecurityManager() == null){            System.setSecurityManager(new SecurityManager());        }        try{            Registry registry = LocateRegistry.getRegistry(2002);            Hello obj = (Hello) registry.lookup("Hello");            String message = obj.sayHello();            System.out.println(message);        }catch(Exception e){            System.out.println("HelloClient exception:"+e.getMessage());            e.printStackTrace();        }    }}

客户端应用程序获得对服务器应用程序使用的注册表的引用。然后它查找远程对象的存根并调用它的远程方法sayHello。

4.编译和运行应用

第一步:编译远程接口,服务端文件和客户端文件

javac *.java

第二步:运行服务器
将class文件所在目录添加到classpath中,进入到class文件的根目录。
执行start java HelloImpl 如果执行成功会返回HelloImpl bound in registry

我的项目路径和执行过程如下:

这里写图片描述

这里写图片描述

这里写图片描述

如果执行过程抛出一个java.security.AccessControlException:access denied,需要添加一个安全策略文件:

我这里的文件名是MyPolicy.policy 位置在D盘下

这里写图片描述
文本格式如下:

grant {    permission java.net.SocketPermission "localhost:*","connect,resolve,accept";};

start java -Djava.security.policy=D:/文件路径 HelloImpl

关于这个安全策略有很多解决方案,这篇文章总结得很好:

RMI总结及可能产生的几种异常及解决

第三步: 运行客户端
start java -Djava.security.policy=D:/文件路径 HelloClient
返回结果:Hello,World!!!

原创粉丝点击