RMI程序设计(二)远程接口实现类作为参数传递

介绍

远程调用是一种非常好的程序设计思想和技术,是现代WEB应用的前驱,也是目前许多基于C/S架构的APP应用软件的主流技术。

客户端可以调用服务端提供的远程服务,服务端也可以回调客户端的远程服务对象,从而实现真正的分布式、协同式软件。一些典型的应用需要服务器调用客户端的对象,如聊天系统中服务器需要刷新客户端的信息显示窗口;股票系统中服务器需要刷新客户端的实时交易数据信息等,因这些动作的事件触发在服务器端,而客户端是被动的。服务器主动刷新客户端的方式,是重要的推送服务程序设计技术。

在上一篇,已经了解了服务器如何主动调用客户端的远程服务(相互的过程),实现方式是客户端发布远程服务(客户端需要发布),由服务端“lookup”其远程服务,进行远程调用;其实还有另外一种使用方式:就是服务端定义的远程服务方法中,提供客户端的远程接口类型参数,客户端采用本地方法调用的方式,将客户端的远程接口实现类作为参数传递给服务端,而不需要特意进行远程服务的发布(将实现类传给服务端,客户端不需要发布)。

程序描述

使用RMI的方式,实现简单的群聊功能。

关键技术:将客户端窗口的信息刷新方法定义成远程接口中的方法,由服务器远程调用实现刷新,从而实现由服务器控制客户聊天窗口信息的显示进程。

程序设计

核心算法

创建两个远程接口ClientService和ServerService,分别由客户端和服务端实现。接口定义如以下代码所示(强调,服务端和客户端都各自拥有相同的远程接口,所在package应一致)。

客户端远程接口ClientService:

package chapter13.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * 客户端远程对象接口,该接口为服务端提供回调服务
 */
public interface ClientService extends Remote
{
    /*
     * 由服务端主动推送消息到客户端,客户端刷新聊天信息
     */
    public void showMsg(String msg) throws RemoteException;
}

服务端远程接口ServerService:

package chapter13_2.rmi;
import java.rmi.Remote;

/**
 * 服务端远程对象接口,该接口为客户端提供服务
 * 由服务端实现以下远程方法
 */
public interface ServerService extends Remote {
    // 客户端调用服务器方法,进行登陆操作
    String login(String client, ClientService clientService) throws RemoteException;
}

创建客户端远程接口实现类:

package chapter13_2.client;

import chapter13_2.rmi.ClientService;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class ClientServiceImpl extends UnicastRemoteObject implements ClientService {

    protected ClientServiceImpl() throws RemoteException {
    }

    @Override
    public void showMsg(String msg) throws RemoteException {
        //客户端刷新窗体信息显示的方法
        System.out.println(msg);
        //...
    }
}

创建服务端远程接口实现类:

package chapter13_2.server;

import chapter13_2.rmi.ClientService;
import chapter13_2.rmi.ServerService;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.ConcurrentHashMap;

public class ServerServiceImpl extends UnicastRemoteObject implements ServerService{

    //存储在线用户的map
    private static ConcurrentHashMap<String, ClientService> onlineGroup = new ConcurrentHashMap<>();

    // constructor
    public ServerServiceImpl() throws RemoteException {
    }
    // 实现接口的方法
    @Override
    public String login(String client, ClientService clientService) throws RemoteException {

        boolean isLogin = false;
        //避免反复登录,关键是判断在线map中是否已经存在相同的clientService
        for (String onlineUser : onlineGroup.keySet()) {
            ClientService cs = onlineGroup.get(onlineUser);
            if(cs == clientService){
                isLogin = true;
            }
        }
        // 如果没登陆
        if (!isLogin) {
            // 加入到用户map中
            onlineGroup.put(client.trim(), clientService);
            // 群发新用户上线的信息
            return "From 服务器:" + client.trim() + " 登陆!";
        }
        else {
            return "From 服务器:不要反复登录";
        }
    }
}

创建服务端的远程服务发布程序:

package chapter13_2.server;

import chapter13_2.rmi.ServerService;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server {
    public static void main(String[] args) throws RemoteException {
        try {
            //对于有多个网卡的机器,建议用下面的命令指绑定固定的ip,因为默认是绑定到0.0.0.0
            //System.setProperty("java.rmi.server.hostname",本机器的ip地址);
            //第一步,启动RMI注册器
            Registry registry = LocateRegistry.createRegistry(8008);
            //第二步,实例化远程服务对象
            ServerService serverService = new ServerServiceImpl();
            //第三步,用助记符来注册及发布远程服务对象,助记符建议和远程服务接口命名相同,方便使用
            registry.rebind("ServerService", serverService);
            System.out.println("服务端发布了ServerService远程服务");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

客户端测试程序:

package chapter13_2.client;
import chapter13_2.rmi.ClientService;
import chapter13_2.rmi.ServerService;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ClientTest {
    public static void main(String[] args) throws RemoteException, NotBoundException {
        String ip = "localhost";

        //为了不和上一讲端口冲突,临时修改为8008,一般不做特别说明,是使用1099
        int port = 8008;
        //获取RMI注册器
        Registry registry = LocateRegistry.getRegistry(ip, port);
        for (String name : registry.list()) {
            System.out.println(name);
        }

        //客户端(调用端)到注册器中使用助记符寻找并创建远程服务对象的客户端(调用端)stub,
        // 之后本地调用serverService的方法,实质就是调用了远程同名接口下的同名方法
        ServerService serverService = (ServerService) registry.lookup("ServerService");
        ClientService clientService = new ClientServiceImpl();
        String res = serverService.login("Ananyo", clientService);
        System.out.println(res);

    }
}

启动服务器,和测试代码。

效果展示:
在这里插入图片描述
我们将自己的远程接口实现类作为参数传递给服务端,而不需要特意进行远程服务的发布,这里有别于RMI程序设计(一)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值