• RMI


    RMI基础
    一 简介
    1.RMI,Remote Method Invocation,即远程方法调用,是Java远程访问的重要规范之一,允许java程序直接调用远程java方法,就像调用该本地java方法一样。Java RMI不是什么新技术,但却是非常重要的底层技术。大名鼎鼎的EJB都是建立在RMI基础之上的,现在还有一些开源的远程调用组件,其底层技术也是RMI。
    2.远程访问:网络中的一台计算机,可以使用另一台计算机的服务、对象或方法,效果就像使用本地的服务、对象和方法一样。通过远程访问,程序可以在物理位置不同的机器上运行,从而实现分布式计算
    3.RMI是纯JAVA实现的,无需其他额外的支持,实现RMI调用的客户端程序和被调用的方法,都是纯JAVA代码,因此实现起来很简单。在JDK1.5发布以前,读者需要手动使用rmic命名(在JDK安装路径的bin路径下)来编译远程服务类,从而为远程服务类生成静态的Stub和Skeleton类。在JDK1.5及以后的版本中,采用了动态方式来自动生成Stub和Skeleton类,简化了RMI编程。
    4.RMI可以说是Java最早实现的远程访问技术,而且性能十分优秀,一直是其他Java EE规范的实现基础。掌握RMI的理论和编程方法,对于开发者更好的理解EJB等分布式组件的底层实现具有重要意义。
    RMI其实是对Java Socket网络通信的高度封装,如果应用程序提供基于Socket的网络通信功能,都可以考虑使用RMI实现。
    当使用原生Socket进行网络编程时,有两个重要的问题:
    网络信息的交换问题:需要采用合适的IO流进行网络读写,如果需要进行复杂的网络信息交换,往往还要封装自己的通信协议
    多线程问题:服务器和客户端的网络程序,两端都要采用多线程功能。编写多线程就不可避免的要处理线程同步、线程安全、死锁等棘手问题。
    而使用RMI来开发,以上的问题就会迎刃而解,可以简化开发难度,提高开发效率。
    二 基本原理
    JDK1.5以后动态生成Stub对象来作为客户端调用远程方法的代理,由于Stub是根据远程服务类来动态生成的,因此当远程服务端的服务类改变时,系统会动态的改变所生成的Stub对象。
    由图中可以看出,当客户端面向远程服务接口调用远程方法后,会有以下几个步骤:
    (1)本地客户端调用远程服务对象的方法,实际上是调用Stub对象的方法。
    (2)Stub对象实际上是远程服务对象在客户端的代理。Stub对象会对请求进行编码,保证远程调用请求可以在网络上传输,所以要求调用远程方法的所有参数都是可序列化的。
    (3)通过底层网络传输将请求传递到Skeleton
    (4)Skeleton对请求进行解码,将请求转换为满足远程服务对象要求的请求。
    (5)Skeleton将解码后的请求发送到远程服务对象,让远程服务对象来负责处理调用请求。
    (6)Skeleton收到远程服务对象的执行结果,再次对执行结果进行编码,这就要求RMI中的方法返回值都是可序列化的。
    (7)通过底层网络传输将结果送到Stub。
    (8)Stub解码处理结果
    (9)Stub将解码后的符合本地客户端要求的结果送到本地客户端
    (10)本地客户端收到执行结果
    三 动手
    开发RMI服务器
    Step1:RMI需要通过远程接口暴露服务,因此所有向北客户端调用的方法都必须在Remote接口里声明,否则无法调用。
    远程接口如下:
    public interface Server extends Remote{
        String helloWorld(String name) throws RemoteException;
        Person getPerson(String name,int age) throws RemoteException;
    }
    注意:远程接口必须集成java.rmi.Remote接口。所有在Remote接口里声明的方法都应该抛出RemoteException异常。
    这是因为远程接口中的方法会通过网络传输,而网络传输又是不可靠的,因此都要抛出一个异常。
    Step2:定义一个简单的Person类
    public class Person implements Serializable{
        private String name;
        private int age;
        ...//省略getter/setter等方法
    }
    Step3:远程服务实现类
    public class ServerImpl extends UnicastRemoteObject implements Server{
     
        //由于默认构造器必须声明抛出RemoteException
        //因此此处必须显示定义该构造器
        public ServerImpl() throws RemoteException{}
     
        @Override
        public String helloWorld(String name) throws RemoteException {
            return name+", hello";
        }
     
        @Override
        public Person getPerson(String name, int age) throws RemoteException {
            return new Person(name,age);
        }
     
        //下面是服务类的本地方法,不会暴露为远程服务
        public void info(){
            System.out.println("这是本地方法");
        }
     
        //提供程序入口,将远程类实例绑定为本机的服务
        public static void main(String[] args) throws Exception{
            try {
            Server imp=new ServerImpl();
            LocateRegistry.createRegistry(8888);
            Naming.rebind("rmi://localhost:8888//myTest",imp);
            }catch (RemoteException e) {
                System.out.println("创建远程对象发生异常!");
                e.printStackTrace();
            } catch (AlreadyBoundException e) {
                System.out.println("发生重复绑定对象异常!");
                e.printStackTrace();
            } catch (MalformedURLException e) {
                System.out.println("发生URL畸形异常!");
                e.printStackTrace();
            }
        }
     
    }
    注意:远程服务类必须继承UnicastRemoteObject,并实现Remote接口,所有继承java.rmi.server.UnicastRemoteObject的对象可以暴露远程服务。
    开发RMI客户端
    public class RMIClient {
        public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
            Server ser=(Server) Naming.lookup("rmi://127.0.0.1:8888/Server");
            System.out.println(ser.helloWorld("lyy"));
            System.out.println(ser.getPerson("lyy",20));
        }
    }
    运行时注意,先运行服务器端,在运行客户端。
    结果:
    lyy, hello
    Person [name=lyy, age=20]
    四 同时作为客户端和服务器的RMI程序
    很多程序,客户端需要调用服务器的远程方法,服务器端也要反过来调用客户端的远程方法,这样才能形成一个完整的网络通信程序。但是服务器端和客户端存在很多区别,因此服务器端不大可能直接调用远程客户端的方法,只能先由客户端调用服务器端的远程方法,这样服务器端才可以回过来调用远程客户端的方法,这种调用方法也被成为回调(callback)。
    举例:聊天窗口
    客户端
    建包client
    Step1:由于客户端也要被远程服务器回调,因此客户端程序也要提供远程服务。
    public interface Client extends Remote {
        void showDialog(String msg) throws java.rmi.RemoteException;
    }
    Step2:服务实现
    public class RMIClient implements Client{
     
        public static void main(String[] args) throws Exception{
            Client client = new RMIClient();
            UnicastRemoteObject.exportObject(client);
            Server stub=(Server)Naming.lookup("rmi://127.0.0.1:8888/Server");
            BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
            String line=null;
            while((line=br.readLine())!=null){
                //调用远程方法时,将自身作为参数
                stub.hello(client,line);
            }
        }
     
        @Override
        public void showDialog(String msg) throws RemoteException {
            System.out.println(msg);
        }
     
    }
    服务器端程序
    建包server
    Step1:接口
    public interface Server extends Remote{
        //定义允许远程调用的方法
        void hello(Client client,String saying) throws Exception;
    }
    Step2:实现
    public class ServerImpl extends UnicastRemoteObject implements Server{
     
        public ServerImpl() throws RemoteException{}
     
        //定义一个List保存所有连接进来的客户
        static List<Client> users=new ArrayList<Client>();
     
        //提供程序入口,将远程类实例绑定为本机的服务
        public static void main(String[] args) throws Exception{
            try {
            Server imp=new ServerImpl();
            LocateRegistry.createRegistry(8888);
            Naming.rebind("rmi://127.0.0.1:8888/Server",imp);
            }catch (RemoteException e) {
                System.out.println("创建远程对象发生异常!");
                e.printStackTrace();
            } catch (AlreadyBoundException e) {
                System.out.println("发生重复绑定对象异常!");
                e.printStackTrace();
            } catch (MalformedURLException e) {
                System.out.println("发生URL畸形异常!");
                e.printStackTrace();
            }
        }
     
     
     
        @Override
        public void hello(Client client, String saying) throws Exception {
            if(!users.contains(client)){
                users.add(client);
            }
            try{
                java.util.Date now=new java.util.Date();
                String msg=now+saying;
                for(Client c:users){
                    //回调远程客户端方法
                    c.showDialog(msg);
                }
            }catch(RemoteException ex){
                users.remove(client);
            }
        }
     
    }
  • 相关阅读:
    一次友情协助的渗透测试
    jQuery---微博发布案例
    jQuery---清空节点和删除节点
    jQuery---城市选择案例
    jQuery---创建和添加节点
    jQuery---动态创建节点
    jQuery---音乐导航
    jQuery---停止动画详解 stop();
    jQuery---手风琴案例+stop的使用(解决动画队列的问题)
    jQuery---自定义动画 animate();
  • 原文地址:https://www.cnblogs.com/lyy-2016/p/5747063.html
Copyright © 2020-2023  润新知