preface
Since the beginning of data mining, I haven't written blog about technology for a long time. Recently, school JAVA The course design requires to realize a chat room. I think I wrote one last year, but I didn't realize some of the required functions, but looking at the original code, it seems that it's a little bit difficult to add functions, so I thought about reconstruction. I saw the CS architecture code written by others before, and I felt that the expansibility was good, so I tried to write it. After writing this chat room At the same time, I also wrote a teaching whiteboard. The whiteboard code based on this chat room took only three or four hours to complete! So it's important to have a good architecture. Let's start with my reconstructed chat room( Code uploaded to github)
Function introduction
1. Write chat server and client with Java GUI, and support multiple clients to connect to one server. Each client can input account, including registration function.
2. Group chat can be realized (chat record is displayed in all client interfaces).
3. Complete the display of friends list on each client, including the avatar and user name.
4. It can realize private chat, and users can select a certain other user to send information separately. At the same time, it can realize file transmission and send window vibration.
5. The server can send system messages in groups, private messages to users, and force some users to go offline.
6. When the client is online or offline, it is required to be able to refresh in real time on other clients.
7. The server can view online users and registered users
(those underlined are beyond the requirements of the course)
Overall thinking
After counting, I wrote 27 classes in total, which seems to be quite a lot, but it's very simple to look at them carefully. I'll explain some of them below
Tools
In several socket communication projects I wrote before, the client and the server transmit strings. This time, I encapsulate the content to be transmitted into two classes: Response and Request: the client sends a request to the server, and the server responds to the client. The operation to be performed is determined by the request type contained in the two classes. ObjectStream is used for transmission. If you look closely, you will find that the contents of these two classes are very similar
Request
1 public class Request implements Serializable { 2 private static final long serialVersionUID = -1237018286305074249L; 3 /** Data type requested for transfer */ 4 private ResponseType type; 5 /** Request action */ 6 private String action; 7 /** Data in request domain, 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 /** Response status */ 4 private ResponseStatus status; 5 /** Type of response data */ 6 private ResponseType type; 7 8 private Map<String, Object> dataMap; 9 10 /** Response output stream */ 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 }
In the above two classes, the content of transmission will include files and messages. For files and messages, we need to know who is the sender and the receiver, the sending time, and so on, so we also encapsulate them into two classes
FileInfo
1 public class FileInfo implements Serializable { 2 private static final long serialVersionUID = -5394575332459969403L; 3 /** Message Receiver */ 4 private User toUser; 5 /** message sender */ 6 private User fromUser; 7 /** Source filename */ 8 private String srcName; 9 /** Send time */ 10 private Date sendTime; 11 /** Destination IP */ 12 private String destIp; 13 /** Destination port */ 14 private int destPort; 15 /** Destination filename */ 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 /** Message Receiver */ 4 private User toUser; 5 /** message sender */ 6 private User fromUser; 7 /** Message content */ 8 private String message; 9 /** Send time */ 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
The User class is used to store User information, because it will be used for transmission, and the serialization transmission is required
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 = "Unnamed"; 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 }
The remaining classes will not be introduced one by one. You can go to my github Source code found on.
Server side
The classes used in the server-side code are shown above. Two classes in entity and ServerInfoFrame are only used for the interface, so they will not be introduced.
UserService
It is used for user account management. Several accounts are created in advance and then saved in the file. Each time the server executes, the account information in the file will be read in, and the newly created user account will also be saved in the file.
1 public class UserService { 2 private static int idCount = 3; //id 3 4 /** New users */ 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 /** User login */ 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 /** Load users by 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 } 34 } 35 return result; 36 } 37 38 39 /** Load all users */ 40 @SuppressWarnings("unchecked") 41 public List<User> loadAllUser() { 42 List<User> list = null; 43 ObjectInputStream ois = null; 44 try { 45 ois = new ObjectInputStream( 46 new FileInputStream( 47 DataBuffer.configProp.getProperty("dbpath"))); 48 49 list = (List<User>)ois.readObject(); 50 } catch (Exception e) { 51 e.printStackTrace(); 52 }finally{ 53 IOUtil.close(ois); 54 } 55 return list; 56 } 57 58 private void saveAllUser(List<User> users) { 59 ObjectOutputStream oos = null; 60 try { 61 oos = new ObjectOutputStream( 62 new FileOutputStream( 63 DataBuffer.configProp.getProperty("dbpath"))); 64 //Write back user information 65 oos.writeObject(users); 66 oos.flush(); 67 } catch (Exception e) { 68 e.printStackTrace(); 69 }finally{ 70 IOUtil.close(oos); 71 } 72 } 73 74 75 76 /** Initialize several test users */ 77 public void initUser(){ 78 User user = new User("admin", "Admin", 'm', 0); 79 user.setId(1); 80 81 User user2 = new User("123", "yong", 'm', 1); 82 user2.setId(2); 83 84 User user3 = new User("123", "anni", 'f', 2); 85 user3.setId(3); 86 87 List<User> users = new CopyOnWriteArrayList<User>(); 88 users.add(user); 89 users.add(user2); 90 users.add(user3); 91 92 this.saveAllUser(users); 93 } 94 95 public static void main(String[] args){ 96 new UserService().initUser(); 97 List<User> users = new UserService().loadAllUser(); 98 for (User user : users) { 99 System.out.println(user); 100 } 101 } 102 }
DataBuffer
It is used by the server to read data from the file and cache it
1 public class DataBuffer { 2 // Server socket 3 public static ServerSocket serverSocket; 4 //For online users IO Map 5 public static Map<Long, OnlineClientIOCache> onlineUserIOCacheMap; 6 //Online users Map 7 public static Map<Long, User> onlineUsersMap; 8 //Server configuration parameter property set 9 public static Properties configProp; 10 // Of registered user tables Model 11 public static RegistedUserTableModel registedUserTableModel; 12 // Of the current online user table Model 13 public static OnlineUserTableModel onlineUserTableModel; 14 // Screen size of the system where the current server is located 15 public static Dimension screenSize; 16 17 static{ 18 // initialization 19 onlineUserIOCacheMap = new ConcurrentSkipListMap<Long,OnlineClientIOCache>(); 20 onlineUsersMap = new ConcurrentSkipListMap<Long, User>(); 21 configProp = new Properties(); 22 registedUserTableModel = new RegistedUserTableModel(); 23 onlineUserTableModel = new OnlineUserTableModel(); 24 screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 25 26 // Load server profile 27 try { 28 configProp.load(Thread.currentThread() 29 .getContextClassLoader() 30 .getResourceAsStream("serverconfig.properties")); 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 36 }
RequestProcessor
At this time, the most important class on the server side is used to process the messages sent by the client side and reply to them. The implementation principle of each operation is nothing more than that the server processes internal data or sends messages to the specified client side. See the code comments in detail
1 public class RequestProcessor implements Runnable { 2 private Socket currentClientSocket; //Client currently requesting server Socket 3 4 public RequestProcessor(Socket currentClientSocket){ 5 this.currentClientSocket = currentClientSocket; 6 } 7 8 public void run() { 9 boolean flag = true; //Continuous monitoring or not 10 try{ 11 OnlineClientIOCache currentClientIOCache = new OnlineClientIOCache( 12 new ObjectInputStream(currentClientSocket.getInputStream()), 13 new ObjectOutputStream(currentClientSocket.getOutputStream())); 14 while(flag){ //Constantly reading the request object sent by the client 15 //Read the request object submitted by the client from the request input stream 16 Request request = (Request)currentClientIOCache.getOis().readObject(); 17 System.out.println("Server Read client's request:" + request.getAction()); 18 19 String actionName = request.getAction(); //Get action in request 20 if(actionName.equals("userRegiste")){ //User registration 21 registe(currentClientIOCache, request); 22 }else if(actionName.equals("userLogin")){ //User login 23 login(currentClientIOCache, request); 24 }else if("exit".equals(actionName)){ //Request to disconnect 25 flag = logout(currentClientIOCache, request); 26 }else if("chat".equals(actionName)){ //chat 27 chat(request); 28 }else if("shake".equals(actionName)){ //Vibration 29 shake(request); 30 }else if("toSendFile".equals(actionName)){ //Ready to send file 31 toSendFile(request); 32 }else if("agreeReceiveFile".equals(actionName)){ //Agree to receive documents 33 agreeReceiveFile(request); 34 }else if("refuseReceiveFile".equals(actionName)){ //Reject file 35 refuseReceiveFile(request); 36 } 37 } 38 }catch(Exception e){ 39 e.printStackTrace(); 40 } 41 } 42 43 /** Reject file */ 44 private void refuseReceiveFile(Request request) throws IOException { 45 FileInfo sendFile = (FileInfo)request.getAttribute("sendFile"); 46 Response response = new Response(); //Create a response object 47 response.setType(ResponseType.REFUSERECEIVEFILE); 48 response.setData("sendFile", sendFile); 49 response.setStatus(ResponseStatus.OK); 50 //Output response to the requester's output stream 51 OnlineClientIOCache ocic = DataBuffer.onlineUserIOCacheMap.get(sendFile.getFromUser().getId()); 52 this.sendResponse(ocic, response); 53 } 54 55 /** Agree to receive documents */ 56 private void agreeReceiveFile(Request request) throws IOException { 57 FileInfo sendFile = (FileInfo)request.getAttribute("sendFile"); 58 //To the requesting party(Sender)Output stream output response of 59 Response response = new Response(); //Create a response object 60 response.setType(ResponseType.AGREERECEIVEFILE); 61 response.setData("sendFile", sendFile); 62 response.setStatus(ResponseStatus.OK); 63 OnlineClientIOCache sendIO = DataBuffer.onlineUserIOCacheMap.get(sendFile.getFromUser().getId()); 64 this.sendResponse(sendIO, response); 65 66 //Send the response of receiving file to the receiver 67 Response response2 = new Response(); //Create a response object 68 response2.setType(ResponseType.RECEIVEFILE); 69 response2.setData("sendFile", sendFile); 70 response2.setStatus(ResponseStatus.OK); 71 OnlineClientIOCache receiveIO = DataBuffer.onlineUserIOCacheMap.get(sendFile.getToUser().getId()); 72 this.sendResponse(receiveIO, response2); 73 } 74 75 /** Client exit */ 76 public boolean logout(OnlineClientIOCache oio, Request request) throws IOException{ 77 System.out.println(currentClientSocket.getInetAddress().getHostAddress() 78 + ":" + currentClientSocket.getPort() + "be gone"); 79 80 User user = (User)request.getAttribute("user"); 81 //Put the current online client's IO from Map Delete from 82 DataBuffer.onlineUserIOCacheMap.remove(user.getId()); 83 //Cache from online users Map Delete current user from 84 DataBuffer.onlineUsersMap.remove(user.getId()); 85 86 Response response = new Response(); //Create a response object 87 response.setType(ResponseType.LOGOUT); 88 response.setData("logoutUser", user); 89 oio.getOos().writeObject(response); //Write the response object to the client 90 oio.getOos().flush(); 91 currentClientSocket.close(); //Close this client Socket 92 93 DataBuffer.onlineUserTableModel.remove(user.getId()); //List current offline users from online users Model Delete from 94 iteratorResponse(response);//Notify all other online clients 95 96 return false; //Disconnect monitor 97 } 98 /** register */ 99 public void registe(OnlineClientIOCache oio, Request request) throws IOException { 100 User user = (User)request.getAttribute("user"); 101 UserService userService = new UserService(); 102 userService.addUser(user); 103 104 Response response = new Response(); //Create a response object 105 response.setStatus(ResponseStatus.OK); 106 response.setData("user", user); 107 108 oio.getOos().writeObject(response); //Write the response object to the client 109 oio.getOos().flush(); 110 111 //Add new registered users to RegistedUserTableModel in 112 DataBuffer.registedUserTableModel.add(new String[]{ 113 String.valueOf(user.getId()), 114 user.getPassword(), 115 user.getNickname(), 116 String.valueOf(user.getSex()) 117 }); 118 } 119 120 /** Sign in */ 121 public void login(OnlineClientIOCache currentClientIO, Request request) throws IOException { 122 String idStr = (String)request.getAttribute("id"); 123 String password = (String) request.getAttribute("password"); 124 UserService userService = new UserService(); 125 User user = userService.login(Long.parseLong(idStr), password); 126 127 Response response = new Response(); //Create a response object 128 if(null != user){ 129 if(DataBuffer.onlineUsersMap.containsKey(user.getId())){ //The user is already logged in 130 response.setStatus(ResponseStatus.OK); 131 response.setData("msg", "The user is already online elsewhere!"); 132 currentClientIO.getOos().writeObject(response); //Write the response object to the client 133 currentClientIO.getOos().flush(); 134 }else { //Log in correctly 135 DataBuffer.onlineUsersMap.put(user.getId(), user); //Add to online users 136 137 //Set up online users 138 response.setData("onlineUsers", 139 new CopyOnWriteArrayList<User>(DataBuffer.onlineUsersMap.values())); 140 141 response.setStatus(ResponseStatus.OK); 142 response.setData("user", user); 143 currentClientIO.getOos().writeObject(response); //Write the response object to the client 144 currentClientIO.getOos().flush(); 145 146 //Notify other users that someone is online 147 Response response2 = new Response(); 148 response2.setType(ResponseType.LOGIN); 149 response2.setData("loginUser", user); 150 iteratorResponse(response2); 151 152 //Put the current online users IO Add to cache Map in 153 DataBuffer.onlineUserIOCacheMap.put(user.getId(),currentClientIO); 154 155 //Add current online users to OnlineUserTableModel in 156 DataBuffer.onlineUserTableModel.add( 157 new String[]{String.valueOf(user.getId()), 158 user.getNickname(), 159 String.valueOf(user.getSex())}); 160 } 161 }else{ //Login failed 162 response.setStatus(ResponseStatus.OK); 163 response.setData("msg", "Incorrect account or password!"); 164 currentClientIO.getOos().writeObject(response); 165 currentClientIO.getOos().flush(); 166 } 167 } 168 169 /** chat */ 170 public void chat(Request request) throws IOException { 171 Message msg = (Message)request.getAttribute("msg"); 172 Response response = new Response(); 173 response.setStatus(ResponseStatus.OK); 174 response.setType(ResponseType.CHAT); 175 response.setData("txtMsg", msg); 176 177 if(msg.getToUser() != null){ //Private chat:Only respond to private chat objects 178 OnlineClientIOCache io = DataBuffer.onlineUserIOCacheMap.get(msg.getToUser().getId()); 179 sendResponse(io, response); 180 }else{ //Group chat:Returns a response to all clients except those sending messages 181 for(Long id : DataBuffer.onlineUserIOCacheMap.keySet()){ 182 if(msg.getFromUser().getId() == id ){ continue; } 183 sendResponse(DataBuffer.onlineUserIOCacheMap.get(id), response); 184 } 185 } 186 } 187 188 /*radio broadcast*/ 189 public static void board(String str) throws IOException { 190 User user = new User(1,"admin"); 191 Message msg = new Message(); 192 msg.setFromUser(user); 193 msg.setSendTime(new Date()); 194 195 DateFormat df = new SimpleDateFormat("HH:mm:ss"); 196 StringBuffer sb = new StringBuffer(); 197 sb.append(" ").append(df.format(msg.getSendTime())).append(" "); 198 sb.append("System notification\n "+str+"\n"); 199 msg.setMessage(sb.toString()); 200 201 Response response = new Response(); 202 response.setStatus(ResponseStatus.OK); 203 response.setType(ResponseType.BOARD); 204 response.setData("txtMsg", msg); 205 206 for (Long id : DataBuffer.onlineUserIOCacheMap.keySet()) { 207 sendResponse_sys(DataBuffer.onlineUserIOCacheMap.get(id), response); 208 } 209 } 210 211 /*Kick user*/ 212 public static void remove(User user_) throws IOException{ 213 User user = new User(1,"admin"); 214 Message msg = new Message(); 215 msg.setFromUser(user); 216 msg.setSendTime(new Date()); 217 msg.setToUser(user_); 218 219 StringBuffer sb = new StringBuffer(); 220 DateFormat df = new SimpleDateFormat("HH:mm:ss"); 221 sb.append(" ").append(df.format(msg.getSendTime())).append(" "); 222 sb.append("System informs you\n "+"You are forced to go offline"+"\n"); 223 msg.setMessage(sb.toString()); 224 225 Response response = new Response(); 226 response.setStatus(ResponseStatus.OK); 227 response.setType(ResponseType.REMOVE); 228 response.setData("txtMsg", msg); 229 230 OnlineClientIOCache io = DataBuffer.onlineUserIOCacheMap.get(msg.getToUser().getId()); 231 sendResponse_sys(io, response); 232 } 233 234 /*private letter*/ 235 public static void chat_sys(String str,User user_) throws IOException{ 236 User user = new User(1,"admin"); 237 Message msg = new Message(); 238 msg.setFromUser(user); 239 msg.setSendTime(new Date()); 240 msg.setToUser(user_); 241 242 DateFormat df = new SimpleDateFormat("HH:mm:ss"); 243 StringBuffer sb = new StringBuffer(); 244 sb.append(" ").append(df.format(msg.getSendTime())).append(" "); 245 sb.append("System informs you\n "+str+"\n"); 246 msg.setMessage(sb.toString()); 247 248 Response response = new Response(); 249 response.setStatus(ResponseStatus.OK); 250 response.setType(ResponseType.CHAT); 251 response.setData("txtMsg", msg); 252 253 OnlineClientIOCache io = DataBuffer.onlineUserIOCacheMap.get(msg.getToUser().getId()); 254 sendResponse_sys(io, response); 255 } 256 257 /** Send vibration */ 258 public void shake(Request request)throws IOException { 259 Message msg = (Message) request.getAttribute("msg"); 260 261 DateFormat df = new SimpleDateFormat("HH:mm:ss"); 262 StringBuffer sb = new StringBuffer(); 263 sb.append(" ").append(msg.getFromUser().getNickname()) 264 .append("(").append(msg.getFromUser().getId()).append(") ") 265 .append(df.format(msg.getSendTime())).append("\n Sent you a window jitter\n"); 266 msg.setMessage(sb.toString()); 267 268 Response response = new Response(); 269 response.setStatus(ResponseStatus.OK); 270 response.setType(ResponseType.SHAKE); 271 response.setData("ShakeMsg", msg); 272 273 OnlineClientIOCache io = DataBuffer.onlineUserIOCacheMap.get(msg.getToUser().getId()); 274 sendResponse(io, response); 275 } 276 277 /** Ready to send file */ 278 public void toSendFile(Request request)throws IOException{ 279 Response response = new Response(); 280 response.setStatus(ResponseStatus.OK); 281 response.setType(ResponseType.TOSENDFILE); 282 FileInfo sendFile = (FileInfo)request.getAttribute("file"); 283 response.setData("sendFile", sendFile); 284 //Forward a request from the sender to the receiver 285 OnlineClientIOCache ioCache = DataBuffer.onlineUserIOCacheMap.get(sendFile.getToUser().getId()); 286 sendResponse(ioCache, response); 287 } 288 289 /** Send a response to all online customers */ 290 private void iteratorResponse(Response response) throws IOException { 291 for(OnlineClientIOCache onlineUserIO : DataBuffer.onlineUserIOCacheMap.values()){ 292 ObjectOutputStream oos = onlineUserIO.getOos(); 293 oos.writeObject(response); 294 oos.flush(); 295 } 296 } 297 298 /** Outputs the specified response to the output stream of the specified client IO */ 299 private void sendResponse(OnlineClientIOCache onlineUserIO, Response response)throws IOException { 300 ObjectOutputStream oos = onlineUserIO.getOos(); 301 oos.writeObject(response); 302 oos.flush(); 303 } 304 305 /** Outputs the specified response to the output stream of the specified client IO */ 306 private static void sendResponse_sys(OnlineClientIOCache onlineUserIO, Response response)throws IOException { 307 ObjectOutputStream oos = onlineUserIO.getOos(); 308 oos.writeObject(response); 309 oos.flush(); 310 } 311 }
Client side
Personal feeling when doing this kind of project, the difficulty is in the client. I have considered the interface switching for a long time before, because it involves the login interface, the registration interface and the chat interface, so how to connect the socket of the client with these interfaces is a problem worthy of consideration. At the same time, I also thought about the display method of friends list for a long time, and finally thought of TIM. Here are some of the classes
ClientThread
Client thread, a thread represents a user, processes messages sent by the server, and uses the variable currentFrame to represent the current window.
1 public class ClientThread extends Thread { 2 private JFrame currentFrame; //Current form 3 4 public ClientThread(JFrame frame){ 5 currentFrame = frame; 6 } 7 8 public void run() { 9 try { 10 while (DataBuffer.clientSeocket.isConnected()) { 11 Response response = (Response) DataBuffer.ois.readObject(); 12 ResponseType type = response.getType(); 13 14 System.out.println("Get response content:" + type); 15 if (type == ResponseType.LOGIN) { 16 User newUser = (User)response.getData("loginUser"); 17 DataBuffer.onlineUserListModel.addElement(newUser); 18 19 ChatFrame.onlineCountLbl.setText( 20 "List of online users("+ DataBuffer.onlineUserListModel.getSize() +")"); 21 ClientUtil.appendTxt2MsgListArea("[System message] user"+newUser.getNickname() + "Online!\n"); 22 }else if(type == ResponseType.LOGOUT){ 23 User newUser = (User)response.getData("logoutUser"); 24 DataBuffer.onlineUserListModel.removeElement(newUser); 25 26 ChatFrame.onlineCountLbl.setText( 27 "List of online users("+ DataBuffer.onlineUserListModel.getSize() +")"); 28 ClientUtil.appendTxt2MsgListArea("[System message] user"+newUser.getNickname() + "Offline!\n"); 29 30 }else if(type == ResponseType.CHAT){ //chat 31 Message msg = (Message)response.getData("txtMsg"); 32 ClientUtil.appendTxt2MsgListArea(msg.getMessage()); 33 }else if(type == ResponseType.SHAKE){ //Vibration 34 Message msg = (Message)response.getData("ShakeMsg"); 35 ClientUtil.appendTxt2MsgListArea(msg.getMessage()); 36 new JFrameShaker(this.currentFrame).startShake(); 37 }else if(type == ResponseType.TOSENDFILE){ //Ready to send file 38 toSendFile(response); 39 }else if(type == ResponseType.AGREERECEIVEFILE){ //The other party agrees to receive the documents 40 sendFile(response); 41 }else if(type == ResponseType.REFUSERECEIVEFILE){ //The other party refuses to receive the document 42 ClientUtil.appendTxt2MsgListArea("[File message] the other party refused to receive, file sending failed!\n"); 43 }else if(type == ResponseType.RECEIVEFILE){ //Start receiving files 44 receiveFile(response); 45 }else if(type == ResponseType.BOARD){ 46 Message msg = (Message)response.getData("txtMsg"); 47 ClientUtil.appendTxt2MsgListArea(msg.getMessage()); 48 }else if(type == ResponseType.REMOVE){ 49 ChatFrame.remove(); 50 } 51 } 52 } catch (IOException e) { 53 //e.printStackTrace(); 54 } catch (ClassNotFoundException e) { 55 e.printStackTrace(); 56 } 57 } 58 59 /** send files */ 60 private void sendFile(Response response) { 61 final FileInfo sendFile = (FileInfo)response.getData("sendFile"); 62 63 BufferedInputStream bis = null; 64 BufferedOutputStream bos = null; 65 Socket socket = null; 66 try { 67 socket = new Socket(sendFile.getDestIp(),sendFile.getDestPort());//socket connection 68 bis = new BufferedInputStream(new FileInputStream(sendFile.getSrcName()));//File read in 69 bos = new BufferedOutputStream(socket.getOutputStream());//Document writing out 70 71 byte[] buffer = new byte[1024]; 72 int n = -1; 73 while ((n = bis.read(buffer)) != -1){ 74 bos.write(buffer, 0, n); 75 } 76 bos.flush(); 77 synchronized (this) { 78 ClientUtil.appendTxt2MsgListArea("[File message] file sending completed!\n"); 79 } 80 } catch (IOException e) { 81 e.printStackTrace(); 82 }finally{ 83 IOUtil.close(bis,bos); 84 SocketUtil.close(socket); 85 } 86 } 87 88 /** receive files */ 89 private void receiveFile(Response response) { 90 final FileInfo sendFile = (FileInfo)response.getData("sendFile"); 91 92 BufferedInputStream bis = null; 93 BufferedOutputStream bos = null; 94 ServerSocket serverSocket = null; 95 Socket socket = null; 96 try { 97 serverSocket = new ServerSocket(sendFile.getDestPort()); 98 socket = serverSocket.accept(); //receive 99 bis = new BufferedInputStream(socket.getInputStream());//Buffer reading 100 bos = new BufferedOutputStream(new FileOutputStream(sendFile.getDestName()));//Buffer write out 101 102 byte[] buffer = new byte[1024]; 103 int n = -1; 104 while ((n = bis.read(buffer)) != -1){ 105 bos.write(buffer, 0, n); 106 } 107 bos.flush(); 108 synchronized (this) { 109 ClientUtil.appendTxt2MsgListArea("[File message] file receiving completed!Store in[" 110 + sendFile.getDestName()+"]\n"); 111 } 112 113 } catch (IOException e) { 114 e.printStackTrace(); 115 }finally{ 116 IOUtil.close(bis,bos); 117 SocketUtil.close(socket); 118 SocketUtil.close(serverSocket); 119 } 120 } 121 122 /** Ready to send file */ 123 private void toSendFile(Response response) { 124 FileInfo sendFile = (FileInfo)response.getData("sendFile"); 125 126 String fromName = sendFile.getFromUser().getNickname() 127 + "(" + sendFile.getFromUser().getId() + ")"; 128 String fileName = sendFile.getSrcName() 129 .substring(sendFile.getSrcName().lastIndexOf(File.separator)+1); 130 131 int select = JOptionPane.showConfirmDialog(this.currentFrame, 132 fromName + " Send files to you [" + fileName+ "]!\n Do you agree to accept?", 133 "receive files", JOptionPane.YES_NO_OPTION); 134 try { 135 Request request = new Request(); 136 request.setAttribute("sendFile", sendFile); 137 138 if (select == JOptionPane.YES_OPTION) { 139 JFileChooser jfc = new JFileChooser(); 140 jfc.setSelectedFile(new File(fileName)); 141 int result = jfc.showSaveDialog(this.currentFrame); 142 143 if (result == JFileChooser.APPROVE_OPTION){ 144 //Set destination file name 145 sendFile.setDestName(jfc.getSelectedFile().getCanonicalPath()); 146 //Set the IP And receive file port 147 sendFile.setDestIp(DataBuffer.ip); 148 sendFile.setDestPort(DataBuffer.RECEIVE_FILE_PORT); 149 150 request.setAction("agreeReceiveFile"); 151 // receiveFile(response); 152 ClientUtil.appendTxt2MsgListArea("[File message] you have agreed to receive from " 153 + fromName +" Files for, receiving files ...\n"); 154 } else { 155 request.setAction("refuseReceiveFile"); 156 ClientUtil.appendTxt2MsgListArea("[File message] you have declined to receive from " 157 + fromName +" Documents of!\n"); 158 } 159 } else { 160 request.setAction("refuseReceiveFile"); 161 ClientUtil.appendTxt2MsgListArea("[File message] you have declined to receive from " 162 + fromName +" Documents of!\n"); 163 } 164 165 ClientUtil.sendTextRequest2(request); 166 } catch (IOException e) { 167 e.printStackTrace(); 168 } 169 } 170 }
ClientUtil
For clients to send messages to servers
1 public class ClientUtil { 2 3 /** Send request object and receive response actively */ 4 public static Response sendTextRequest(Request request) throws IOException { 5 Response response = null; 6 try { 7 // Send request 8 DataBuffer.oos.writeObject(request); 9 DataBuffer.oos.flush(); 10 System.out.println("Client sent request object:" + request.getAction()); 11 12 if(!"exit".equals(request.getAction())){ 13 // Get response 14 response = (Response) DataBuffer.ois.readObject(); 15 System.out.println("Client got response object:" + response.getStatus()); 16 }else{ 17 System.out.println("Client disconnected"); 18 } 19 } catch (IOException e) { 20 throw e; 21 } catch (ClassNotFoundException e) { 22 e.printStackTrace(); 23 } 24 return response; 25 } 26 27 /** Send request object, do not actively receive response */ 28 public static void sendTextRequest2(Request request) throws IOException { 29 try { 30 DataBuffer.oos.writeObject(request); // Send request 31 DataBuffer.oos.flush(); 32 System.out.println("Client sent request object:" + request.getAction()); 33 } catch (IOException e) { 34 throw e; 35 } 36 } 37 38 /** Add the specified text to the message list text field */ 39 public static void appendTxt2MsgListArea(String txt) { 40 ChatFrame.msgListArea.append(txt); 41 //Position the cursor to the last line of the text field 42 ChatFrame.msgListArea.setCaretPosition(ChatFrame.msgListArea.getDocument().getLength()); 43 } 44 }
summary
I will introduce these in general details. Most of the rest is interface related code. I will put the whole project into github Yes, I feel that the current framework can adapt to all tasks related to CS architecture arranged by the school. I've learned that it's only a few hours for others to finish in a few days, and it looks more comfortable than others. The next article will introduce the use of this framework to achieve another project - teaching whiteboard.