利用RMI技术构建远程会话详细教程

1、引言

内容为:利用RMI技术构建远程会话:2个客户端发送消息到服务器端,服务器端把相应的消息中转发送给对应的客户,进行远程对话。

2、实验环境

  • 操作系统:Windows 10
  • 实验工具:IntelliJ IDEA
  • 环境:Java jdk 1.8

3、实验过程

3.1 熟悉RMI技术原理

RMI基本原理图如下,应用了代理模式来封装了本地存根与真实的远程对象进行通信的细节。
ALT

3.2熟悉RMI技术构建远程会话之“Hello world”例子

3.2.1 创建远程接口,接口定义了所有的提供远程服务的功能

远程接口需要继承java.rmi.Remote接口;
接口中的所有方法声明抛出java.rmi.RemoteException;

package rmi_test;

import java.rmi.*;

public interface Hello extends java.rmi.Remote{
    String sayHello(String name) throws java.rmi.RemoteException;
}

3.2.2 创建远程类,实现远程接口,是远程服务的具体实现

package rmi_test;

import java.rmi.*;
import java.rmi.server.*;

public class HelloImpl implements Hello{
    public HelloImpl(){}
    public String sayHello(String name){
        return "Hello,"+name+"!";
    }
}

3.2.3 创建服务器程序,Server类是一个简单的服务器

其中,通过UnicastRemoteObject.exportObject(obj,0)方法,将自身导出为远程对象;
通过createRegistry()方法注册远程对象;
通过rebind方法,把远程对象绑定到指定名称空间中;

package rmi_test;

import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;

public class Server{
    public Server() {}
    public static void main(String args[]) {
        System.setSecurityManager(new RMISecurityManager());

        final HelloImpl obj = new HelloImpl();
        try {                               // 0 - anonymous TCP port ↓
            Hello stub = (Hello)UnicastRemoteObject.exportObject(obj, 0);
            // Bind the remote object's stub in the registry
            Registry registry = LocateRegistry.createRegistry(3333);
            registry.rebind("Hello", stub);
            for(int i = 0; i < registry.list().length; i++)
                System.out.println(registry.list()[i]);
            System.err.println("Server ready....");
            System.err.println("Listinging on port 3333 ....");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.2.4 创建客户端程序

通过 lookup()方法查找远程对象,进行远程方法调用;

package rmi_test;

import java.net.MalformedURLException;
import java.rmi.registry.*;
import java.rmi.*;

public class Client {
    private Client() {
    }

    public static void main(String[] args) throws RemoteException, MalformedURLException,NotBoundException {
        String host = (args.length < 1) ? "localhost" : args[0];
        String name = (args.length == 2) ? args[1] : "World";

            String urlo = "rmi://" + host + ":3333/Hello";
            Hello stub = (Hello) Naming.lookup(urlo);
            System.out.println("link to the server: \n" + urlo);
            Registry registry = LocateRegistry.getRegistry(host);
            //Hello stub = (Hello)registry.lookup("Hello");
            String response = stub.sayHello(name);
            System.out.println("Response: " + response);

    }
}

3.2.5 运行结果

服务器端运行结果

客户端运行结果

3.3 实现:利用RMI技术构建远程会话:2个客户端发送消息到服务器端,服务器端把相应的消息中转发送给对应的客户,进行远程对话

3.3.1 创建远程接口,接口定义了所有的提供远程服务的功能

package rmi_test;

import java.rmi.*;

public interface Hello extends java.rmi.Remote{
    //String sayHello(String name) throws java.rmi.RemoteException;
    String getMessage() throws java.rmi.RemoteException;
    void setMessage(String receiveMessage) throws java.rmi.RemoteException;
    void setOne(boolean one)throws java.rmi.RemoteException;
    boolean getOne()throws java.rmi.RemoteException;
    void setTwo(boolean two)throws java.rmi.RemoteException;
    boolean getTwo()throws java.rmi.RemoteException;
}

3.3.2 创建远程类,实现远程接口,是远程服务的具体实现

要实现两个客户端通过远程方法调用进行通信,所以写了针对远程对象成员的get,set方法,以便两个客户端可以进行通过set方法给成员变量赋值,通过get方法获取成员变量的值,以此实现通信;
同时为了区别两个客户端的发送的信息,使用布尔型成员变量one,two区分;

package rmi_test;

import java.rmi.*;
import java.rmi.server.*;

public class HelloImpl implements Hello{
   String message;
   boolean one;
   boolean two;
   public HelloImpl(){
       message=null;
       one=false;
       two=false;
   }
   @Override
   public void setOne(boolean one)
   {
       this.one=one;
   }
   @Override
   public boolean getOne()
   {
       return one;
   }
   @Override
   public void setTwo(boolean two)
   {
       this.two=two;
   }
   @Override
   public boolean getTwo()
   {
       return two;
   }

   @Override
   public String getMessage()throws RemoteException
   {
       return message;
   }

   @Override
   public void setMessage(String m)throws RemoteException
   {
       message=m;
   }
}

3.3.3 创建服务器程序,Server类是一个简单的服务器

原理同“hello world”实例。

package rmi_test;

import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;

public class Server{
    public Server() {}
    public static void main(String args[]) {
        System.setSecurityManager(new RMISecurityManager());

        final HelloImpl obj = new HelloImpl();
        try {                               // 0 - anonymous TCP port ↓
            Hello stub = (Hello)UnicastRemoteObject.exportObject(obj, 0);
            // Bind the remote object's stub in the registry
            Registry registry = LocateRegistry.createRegistry(3333);
            registry.rebind("Hello", stub);
            for(int i = 0; i < registry.list().length; i++)
                System.out.println(registry.list()[i]);
            System.err.println("Server ready....");
            System.err.println("Listinging on port 3333 ....");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3.4 创建客户端程序

原理同“hello world”实例,主要是通过get,set方法修改远程对象成员变量实现客户端之间的通信,因为不能实现实时通信,所以需要客户端主动去查看对方是否发送信息。
客户端一:

package rmi_test;

import java.net.MalformedURLException;
import java.rmi.registry.*;
import java.rmi.*;
import java.util.Scanner;

public class Client1 {
    private Client1() {
    }
    public static void main(String[] args) throws RemoteException, MalformedURLException,NotBoundException {
        String host = (args.length < 1) ? "localhost" : args[0];
        String name = (args.length == 2) ? args[1] : "World";


            String urlo = "rmi://" + host + ":3333/Hello";
            Hello stub = (Hello) Naming.lookup(urlo);
            System.out.println("link to the server: \n" + urlo);
            Registry registry = LocateRegistry.getRegistry(host);
            while(true)
            {
                Scanner scanner=new Scanner(System.in);
                String message=scanner.nextLine();
                stub.setMessage(message);
                stub.setOne(true);
                stub.setTwo(false);
                while(stub.getTwo()==false)
                    ;
                if(stub.getTwo()==true) {
                    String receive = stub.getMessage();

                    System.out.println("Client2 send message: " + receive);
                }
            }
    }
}

客户端二:

package rmi_test;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Scanner;
public class Client2 {
    private Client2() {
    }

    public static void main(String[] args) throws RemoteException, MalformedURLException,NotBoundException {
        String host = (args.length < 1) ? "localhost" : args[0];
        String name = (args.length == 2) ? args[1] : "World";

            String urlo = "rmi://" + host + ":3333/Hello";
            Hello stub = (Hello) Naming.lookup(urlo);
            System.out.println("link to the server: \n" + urlo);
            Registry registry = LocateRegistry.getRegistry(host);
            //Hello stub = (Hello)registry.lookup("Hello");
            while(true)
            {
                while(stub.getOne()==false)
                    ;
                if(stub.getOne()==true) {
                    String receive = stub.getMessage();
                    System.out.println("Client1 send message: " + receive);
                }
                Scanner scanner=new Scanner(System.in);
                String message=scanner.nextLine();
                stub.setMessage(message);
                stub.setTwo(true);
                stub.setOne(false);
            }
    }
}

3.3.5 运行结果截图

服务端截图

客户端一截图:

客户端二截图:

4、 总结

  • 要利用RMI技术实现远程通信首先要清楚RMI的原理,深入理解远程对象的概念;
  • 在实际编程的过程中需要注意的问题:
    • 在编写远程对象接口时:
      • 要继承java.rmi.Remote接口;
      • 接口中的所有方法声明抛出java.rmi.RemoteException;
    • 在编写服务端代码时:
      • 要UnicastRemoteObject.exportObject(this,0)方法,导出为远程对象(也可以在远程对象接口的实现类中导出为远程对象);
      • 通过createRegistry()方法注册远程对象;
      • 通过rebind方法,把远程对象绑定到指定名称空间中;
    • 在编写客户端代码时:
      • 通过 lookup()方法查找远程对象,进行远程方法调用;
  • 运行时需要注意问题:先启动服务端程序,再启动客户端程序。

猜你喜欢

转载自blog.csdn.net/babyx_back/article/details/88652800