RPC模式

Posted 一乐乐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RPC模式相关的知识,希望对你有一定的参考价值。

RPC模式

 

一、概念:

rpc模式:即客户端、服务端之间的信息交流模式,

客户端通过发送请求数据包给服务端,

服务端接收到数据包(拆解数据包),进行业务处理后,返回一个处理过的数据包给客户端,

然后客户端再进行操作。

 

二、流程图

 

三、举例:

❀ 客户端界面(登录界面)

import Client.net.Client;
import Common.entity.BizType;
import Common.entity.User;
import Common.util.SysDto;
/**
 * 登录界面类
 * @author Huangyujun
 */
public class LoginFrame extends JFrame{//登录客户端
    Client client = null;
   //.....界面的东西省略....../**
     * 为登录按纽添加事件(~核心功能~~~)
     */
    private void loginEvent() {
     //btnLogin: 登录按钮 btnLogin.addActionListener(
new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //构建客户端 client = new Client("localhost", 8888); //构建登录业务的DTO(DTO 是数据包对象) SysDto sysDto = new SysDto(BizType.登录);
          //account和 password 是登录界面的账号和密码输入框 String account
= txtLoginName.getText(); String password = txtLoginPwd.getText(); sysDto.setUser(new User(account, password)); //发送 SysDto dto = null; try { dto = client.sendAndReadDto(sysDto); } catch (ClassNotFoundException | IOException e1) { e1.printStackTrace(); } //若登录成功后,进入聊天界面 if(dto.getBizType().equals(BizType.登录成功)) { //打印从服务端处理后的登录用户信息 System.out.println("从服务端处理后的登录用户信息: "); System.out.println("从服务端返回用户账号:" + dto.getUser().getAccount()); System.out.println("从服务端返回用户密码:" + dto.getUser().getPassword()); } } }); } }

 

❀ 客户端

package Client.net;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

import Common.entity.BizType;
import Common.util.SysDto;

/**
 * 客户端
 * @author Huangyujun
 *
 */
public class Client {
    private String host;
    private int port;
    private Socket client;
    private ObjectOutputStream objOutStream;
    private ObjectInputStream objInStream;
    private boolean isRunning;
    
    //构造方法
    public Client(String host, int port) {
        this.host = host;
        this.port = port;
        isRunning = true;
        try {
            client = new Socket(host, port);
            objOutStream = new ObjectOutputStream(client.getOutputStream());
            objInStream = new ObjectInputStream(client.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }    
    }
    
    /**
     * 发送DTO
     * @param sysDto
     * @throws IOException 
     */
    public void sendDto(SysDto sysDto) throws IOException {
        objOutStream.writeObject(sysDto);
        //推流
        objOutStream.flush();
    }

    /**
     * 接收DTO
     * @return
     * @throws ClassNotFoundException
     * @throws IOException
     */
    public SysDto readDto() throws ClassNotFoundException, IOException {
        Object data = objInStream.readObject();
        return (SysDto) data;
    }
    
    /**
     * 发送并接收DTO
     * @param sysDto
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public SysDto sendAndReadDto(SysDto sysDto) throws IOException, ClassNotFoundException {
        sendDto(sysDto);
        return readDto();
    }
    /**
     * 关闭操作方法
     */
    public void close(SysDto sysDto) {
        if(sysDto.getBizType().equals(BizType.退出)) {
            //发送退出数据包给服务器,让服务器断开处理客户端的客户端任务
            try {
                sendDto(sysDto);
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            //退出:关闭各种流和Socket
            try {
                //休息一下,避免与更新界面冲突
                TimeUnit.MILLISECONDS.sleep(5000);
                objInStream.close();
                objOutStream.close();
                client.close();            
            } catch (IOException | InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    public Socket getClient() {
        return client;
    }

    public void setClient(Socket client) {
        this.client = client;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

}

 

❀ 数据包

package Common.util;
import java.io.Serializable;
import Common.entity.BizType;
import Common.entity.User;

/**
 * 数据传输对象,也需要在网络上传输,实现序列化
 * @author Huangyujun
 *
 */
public class SysDto implements Serializable {
    private static final long serialVersonUID = 2;
    private BizType bizType;        //数据传输对象类型
    //先考虑登录业务所需传输的数据对象
    private User user;
  //构造方法
    public SysDto(BizType bizType) {
        this.bizType = bizType;
    }

    public BizType getBizType() {
        return bizType;
    }

    public void setBizType(BizType bizType) {
        this.bizType = bizType;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

 

❀ 服务端

package ServerNet;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import Common.entity.User;
import Common.util.SysDto;

/**
 * 服务端
 * @author Huangyujun
 *
 */
public class Server {
    private int port;                    //端口
    private boolean isRunning;            //运行状态
    private ServerSocket serverSocket;    //服务器
    /** 客户端任务集合 */
    private static List<SocketHandler> listHandler = null;
    
    //构造方法
    public Server(int port) {
        this.port = port;
        isRunning = true;
        listHandler = new ArrayList<SocketHandler>();
    }

    /**
     * 启动方法
     */
    public void start() {
        //构建服务端,并连接
        serverSocket = null;
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务端已经连接,正在监听端口:" + port);
            //循环不断地监听客户端
            while(isRunning) {
                Socket client = serverSocket.accept();
                //创建客户端任务处理客户端业务请求,将当前活跃的客户端传入
            SocketHandler socketHandler = new SocketHandler(client);
            //listHandler 添加socketHandler 客户端任务
                listHandler.add(socketHandler);
                //启动客户端任务线程
                new Thread(socketHandler).start();    
            }    
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public ServerSocket getServerSocket() {
        return serverSocket;
    }

    public void setServerSocket(ServerSocket serverSocket) {
        this.serverSocket = serverSocket;
    }
    
    public List<SocketHandler> getListHandler() {
        return listHandler;
    }
    }

 

❀ 客户端处理器

package ServerNet;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import Common.entity.BizType;
import Common.entity.User;
import Common.util.SysDto;
import ServerBiz.ServerBiz;

/**
 * 客户端任务类
 * @author Huangyujun
 *
 */
public class SocketHandler implements Runnable{
    private Socket client;
    private boolean isRunning;
    private ObjectInputStream objInStream;
    private ObjectOutputStream objOutStream;
    /** 服务端业务处理对象 */
    private ServerBiz serverBiz;
    //当前用户~登录完成就有啦
    private User loginUsers;
    //构造方法
    public SocketHandler(Socket client) {
     //当前客户端
this.client = client; serverBiz = new ServerBiz(); isRunning = true; //获得输入对象流、输出对象流 objInStream = null; try { objInStream = new ObjectInputStream(client.getInputStream()); objOutStream = new ObjectOutputStream(client.getOutputStream()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { //先不断地接收客户端传递的业务数据 Object data = null; try { while((data = objInStream.readObject()) != null){ //1,先判断数据属于什么业务 if(!(data instanceof SysDto)) { System.out.println("未知业务类型,无法处理!"); }else { //2,进行类型转换 SysDto sysDto = (SysDto) data; //3,调用业务类的方法,处理返回处理后的数据包 SysDto dto = serverBiz.dealWithType(sysDto); if(dto.getBizType().equals(BizType.登录成功)) { this.loginUsers = dto.getUser(); } //4,将处理后得到的数据包写回给客户端 objOutStream.writeObject(dto); //5,推流 objOutStream.flush(); if(dto.getBizType().equals(BizType.退出成功)) { //休息10秒钟 Thread.sleep(10000); //设置客户端任务状态为false, isRunning = false; //关闭各种流和Socket objOutStream.close(); objInStream.close(); client.close(); break; } } } } catch (ClassNotFoundException | IOException | InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public boolean isRunning() { return isRunning; } public User getLoginUsers() { return loginUsers; } }

 

❀ 服务业务类

package ServerBiz;

import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import Common.entity.BizType;
import Common.entity.Message;
import Common.entity.User;
import Common.util.SysDto;
import ServerNet.Server;
import ServerNet.SocketHandler;

/**
 * 服务端业务处理类,把传进来的Dto进行处理后返回
 * @author Huangyujun
 *
 */
public class ServerBiz {
    //工厂方法
    public SysDto dealWithType(SysDto sysDto) throws IOException {
        switch(sysDto.getBizType()) {
            case 登录:
                return doLogin(sysDto);
case 退出:
                return doExit(sysDto);
        }
        return null;
    }
/**
     * 登录业务
     * @param sysDto
     * @return
     */
    private SysDto doLogin(SysDto sysDto) {
        //1,验证密码、账号是否正确(没有数据库省略)
        //2,从数据库获取该用户信息
        //3,设置sysDto类型为登录成功
        //4,返回设置了用户信息的sysDto
        String account = sysDto.getUser().getAccount();
        String password = sysDto.getUser().getPassword();
        User user = new User(account, password);
        System.out.println("当前从客户端传递来的用户账号是:" + account + " 密码是:" + password);
        //设置业务类型为登录成功
        sysDto.setBizType(BizType.登录成功);
        System.out.println("当前在线人数:" +Server.getOnlineHandler().size());
        //设置DTO的用户信息
        sysDto.setUser(user);
        return sysDto;
    }

}

 

以上是关于RPC模式的主要内容,如果未能解决你的问题,请参考以下文章

在 GWT-RPC 中将 ArrayList 作为参数发送

尝试使用片段保存夜间模式状态

是否有在单个活动中处理多个片段的 Android 设计模式?

RPC 原理以及开源 RPC 协议 thrift 源码解析

gRPC 的 4 种基础通信模式

RPC模式