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; } }