重构 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 }
Request

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

在以上两个类中,传输的内容会包括文件和消息,对于文件和消息,我们需要直到发送者和接受者是谁,需要知道发送时间等等,所以同样封装成了两个类

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

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

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

剩余的类就不一一介绍了,如果有需要可以到我的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机制,重构线程,将单独的登录和注册线程合并

基于OpenGL编写一个简易的2D渲染框架-08 重构渲染器-整体架构