重构 JAVA 聊天室 —— CS 模式的简单架构实现
Posted ITryagain
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重构 JAVA 聊天室 —— CS 模式的简单架构实现相关的知识,希望对你有一定的参考价值。
前言
自从开始弄起数据挖掘之后,已经很久没写过技术类的博客了,最近学校 JAVA 课设要求实现一个聊天室,想想去年自己已经写了一个了,但是有些要求到的功能我也没实现,但看着原有的代码想了想加功能好像有那么点点难,于是就想着重构,也正好之前有看到别人写的CS架构的代码,感觉扩展性还不错,就试着写了写,写完这个聊天室后,还同时写了一个教学白板,那个白板基于这个聊天室的代码仅仅花了三四个小时就完成了!所以,有一个好的架构还是很重要的。下面就开始介绍我重构后的聊天室(代码已上传到github)
功能介绍
1. 用Java图形用户界面编写聊天室服务器端和客户端, 支持多个客户端连接到一个服务器。每个客户端能够输入账号,包括注册功能。
2. 可以实现群聊(聊天记录显示在所有客户端界面)。
3. 完成好友列表在各个客户端上显示,包括头像和用户名。
4. 可以实现私人聊天,用户可以选择某个其他用户,单独发送信息,同时实现了文件传输,还能发送窗口振动。
5. 服务器能够群发系统消息,能够对用户私发消息,能够强行让某些用户下线。
6. 客户端的上线下线要求能够在其他客户端上面实时刷新。
7.服务器能够查看在线用户和注册用户
(加了下划线的是课设要求之外的)
整体思路
数了数,总共写了27个类,看起来还是蛮多的,但是仔细看一看还是很简单的,我将在下面对其中部分进行解释
工具类
在我之前写的几个socket通信有关的项目里,客户端和服务器传输的都是字符串,而这次,我把要传输的内容封装成了两个类 Response 和 Request,客户端向服务器发起请求,服务器向客户端回应,通过两个类中包含的请求类型来判断需要进行的操作,传输采用ObjectStream。仔细以看其实会发现,这两个类内容很相似
Request
1 public class Request implements Serializable { 2 private static final long serialVersionUID = -1237018286305074249L; 3 /** 请求传送的数据类型 */ 4 private ResponseType type; 5 /** 请求动作 */ 6 private String action; 7 /** 请求域中的数据,name-value */ 8 private Map<String, Object> attributesMap; 9 10 public Request(){ 11 this.attributesMap = new HashMap<String, Object>(); 12 } 13 14 public ResponseType getType() { 15 return type; 16 } 17 18 public void setType(ResponseType type) { 19 this.type = type; 20 } 21 22 public String getAction() { 23 return action; 24 } 25 26 public void setAction(String action) { 27 this.action = action; 28 } 29 30 public Map<String, Object> getAttributesMap() { 31 return attributesMap; 32 } 33 34 public Object getAttribute(String name){ 35 return this.attributesMap.get(name); 36 } 37 38 public void setAttribute(String name, Object value){ 39 this.attributesMap.put(name, value); 40 } 41 42 public void removeAttribute(String name){ 43 this.attributesMap.remove(name); 44 } 45 46 public void clearAttribute(){ 47 this.attributesMap.clear(); 48 } 49 }
Response
1 public class Response implements Serializable { 2 private static final long serialVersionUID = 1689541820872288991L; 3 /** 响应状态 */ 4 private ResponseStatus status; 5 /** 响应数据的类型 */ 6 private ResponseType type; 7 8 private Map<String, Object> dataMap; 9 10 /** 响应输出流 */ 11 private OutputStream outputStream; 12 13 public Response(){ 14 this.status = ResponseStatus.OK; 15 this.dataMap = new HashMap<String, Object>(); 16 } 17 18 19 public ResponseStatus getStatus() { 20 return status; 21 } 22 23 public void setStatus(ResponseStatus status) { 24 this.status = status; 25 } 26 27 public ResponseType getType() { 28 return type; 29 } 30 31 public void setType(ResponseType type) { 32 this.type = type; 33 } 34 35 public Map<String, Object> getDataMap() { 36 return dataMap; 37 } 38 39 public void setDataMap(Map<String, Object> dataMap) { 40 this.dataMap = dataMap; 41 } 42 43 public OutputStream getOutputStream() { 44 return outputStream; 45 } 46 47 public void setOutputStream(OutputStream outputStream) { 48 this.outputStream = outputStream; 49 } 50 51 public void setData(String name, Object value){ 52 this.dataMap.put(name, value); 53 } 54 55 public Object getData(String name){ 56 return this.dataMap.get(name); 57 } 58 59 public void removeData(String name){ 60 this.dataMap.remove(name); 61 } 62 63 public void clearData(){ 64 this.dataMap.clear(); 65 } 66 }
在以上两个类中,传输的内容会包括文件和消息,对于文件和消息,我们需要直到发送者和接受者是谁,需要知道发送时间等等,所以同样封装成了两个类
FileInfo
1 public class FileInfo implements Serializable { 2 private static final long serialVersionUID = -5394575332459969403L; 3 /** 消息接收者 */ 4 private User toUser; 5 /** 消息发送者 */ 6 private User fromUser; 7 /** 源文件名 */ 8 private String srcName; 9 /** 发送时间 */ 10 private Date sendTime; 11 /** 目标地IP */ 12 private String destIp; 13 /** 目标地端口 */ 14 private int destPort; 15 /** 目标文件名 */ 16 private String destName; 17 public User getToUser() { 18 return toUser; 19 } 20 public void setToUser(User toUser) { 21 this.toUser = toUser; 22 } 23 public User getFromUser() { 24 return fromUser; 25 } 26 public void setFromUser(User fromUser) { 27 this.fromUser = fromUser; 28 } 29 public String getSrcName() { 30 return srcName; 31 } 32 public void setSrcName(String srcName) { 33 this.srcName = srcName; 34 } 35 public Date getSendTime() { 36 return sendTime; 37 } 38 public void setSendTime(Date sendTime) { 39 this.sendTime = sendTime; 40 } 41 public String getDestIp() { 42 return destIp; 43 } 44 public void setDestIp(String destIp) { 45 this.destIp = destIp; 46 } 47 public int getDestPort() { 48 return destPort; 49 } 50 public void setDestPort(int destPort) { 51 this.destPort = destPort; 52 } 53 public String getDestName() { 54 return destName; 55 } 56 public void setDestName(String destName) { 57 this.destName = destName; 58 } 59 }
Message
1 public class Message implements Serializable { 2 private static final long serialVersionUID = 1820192075144114657L; 3 /** 消息接收者 */ 4 private User toUser; 5 /** 消息发送者 */ 6 private User fromUser; 7 /** 消息内容 */ 8 private String message; 9 /** 发送时间 */ 10 private Date sendTime; 11 12 13 public User getToUser() { 14 return toUser; 15 } 16 public void setToUser(User toUser) { 17 this.toUser = toUser; 18 } 19 public User getFromUser() { 20 return fromUser; 21 } 22 public void setFromUser(User fromUser) { 23 this.fromUser = fromUser; 24 } 25 public String getMessage() { 26 return message; 27 } 28 public void setMessage(String message) { 29 this.message = message; 30 } 31 32 public Date getSendTime() { 33 return sendTime; 34 } 35 public void setSendTime(Date sendTime) { 36 this.sendTime = sendTime; 37 } 38 }
User
User 类则用于存储用户信息,因为会用于传输,需实现序列化传输
1 public class User implements Serializable { 2 private static final long serialVersionUID = 5942011574971970871L; 3 private long id; 4 private String password; 5 private String nickname; 6 private int head; 7 private char sex; 8 9 public User(String password, String nickname, char sex, int head){ 10 this.password = password; 11 this.sex = sex; 12 this.head = head; 13 if(nickname.equals("")||nickname==null) 14 { 15 this.nickname = "未命名"; 16 }else{ 17 this.nickname = nickname; 18 } 19 } 20 21 public User(long id, String password){ 22 this.id = id; 23 this.password = password; 24 } 25 26 public long getId(){ 27 return id; 28 } 29 30 public void setId(long id){ 31 this.id = id; 32 } 33 34 public void setPassword(String password){ 35 this.password = password; 36 } 37 38 public String getPassword(){ 39 return password; 40 } 41 42 public void setSex(char sex){ 43 this.sex=sex; 44 } 45 46 public char getSex(){ 47 return this.sex; 48 } 49 50 public void setNickname(String nickname){ 51 this.nickname = nickname; 52 } 53 54 public String getNickname(){ 55 return this.nickname; 56 } 57 58 public void setHead(int head){ 59 this.head = head; 60 } 61 62 public int getHead(){ 63 return this.head; 64 } 65 66 public ImageIcon getHeadIcon(){ 67 ImageIcon image = new ImageIcon("images/"+head+".png"); 68 return image; 69 } 70 71 @Override 72 public int hashCode() { 73 final int prime = 31; 74 int result = 1; 75 result = prime * result + head; 76 result = prime * result + (int)(id ^ (id >> 32)); 77 result = prime * result + ((nickname == null) ? 0 : nickname.hashCode()); 78 result = prime * result + ((password == null) ? 0 : password.hashCode()); 79 result = prime * result + sex; 80 return result; 81 } 82 83 @Override 84 public boolean equals(Object obj) { 85 if(this == obj) 86 return true; 87 if(obj == null) 88 return false; 89 if(getClass() != obj.getClass()) 90 return false; 91 User other = (User) obj; 92 if(head != other.head || id != other.id || sex != other.sex) 93 return false; 94 if(nickname == null){ 95 if(other.nickname != null) 96 return false; 97 }else if(!nickname.equals(other.nickname)) 98 return false; 99 if(password == null){ 100 if(other.password != null) 101 return false; 102 }else if(!password.equals(other.password)) 103 return false; 104 return true; 105 } 106 107 @Override 108 public String toString() { 109 return this.getClass().getName() 110 + "[id=" + this.id 111 + ",pwd=" + this.password 112 + ",nickname=" + this.nickname 113 + ",head=" + this.head 114 + ",sex=" + this.sex 115 + "]"; 116 } 117 }
剩余的类就不一一介绍了,如果有需要可以到我的github上找到源代码。
Server端
服务器端的代码用到的类如上所示,其中 entity 中的两个类和 ServerInfoFrame 仅用于界面,所以不会进行介绍。
UserService
用于用户账号管理,预先创建几个账号,然后存到文件中,每次服务器执行时,都会将文件中的账号信息读入,同时新创建的用户账号也会存入到文件中去。
1 public class UserService { 2 private static int idCount = 3; //id 3 4 /** 新增用户 */ 5 public void addUser(User user){ 6 user.setId(++idCount); 7 List<User> users = loadAllUser(); 8 users.add(user); 9 saveAllUser(users); 10 } 11 12 /** 用户登录 */ 13 public User login(long id, String password){ 14 User result = null; 15 List<User> users = loadAllUser(); 16 for (User user : users) { 17 if(id == user.getId() && password.equals(user.getPassword())){ 18 result = user; 19 break; 20 } 21 } 22 return result; 23 } 24 25 /** 根据ID加载用户 */ 26 public User loadUser(long id){ 27 User result = null; 28 List<User> users = loadAllUser(); 29 for (User user : users) { 30 if(id == user.getId()){ 31 result = user; 32 break; 33 } java设计模式7,一看就懂,架构灵魂,工厂方法模式设计模式之简单工厂模式与策略模式(通过两种模式设计的计算器/java)
设计模式之简单工厂模式与策略模式(通过两种模式设计的计算器/java)
设计模式之简单工厂模式与策略模式(通过两种模式设计的计算器/java)
java在线聊天项目1.1版 ——开启多个客户端,分别实现注册和登录功能,使用客户端与服务端信息request机制,重构线程,将单独的登录和注册线程合并