Multiplayer, single chat room version
FEATURE
- Multi-person chat, simple and beautiful interface, use ueditor to support sending text and picture information
- Group chat member list, log in and log out announcement
- Store chat history, view historical messages
technical point
- Use CopyOnWriteMap to store websocketServer objects, thread-safe
- redis stores message records
- ConcurrentLinkedQueue stores chat members
EVERYTHING
- High concurrency is not handled, and high concurrency will put great pressure on the server and memory. The solution is to implement distributed
- Currently, all members are in one chat room, and it is planned to isolate multiple chat rooms according to the chat room ID (using Redis storage)
main logic code
@ServerEndpoint(value="/websocketServer", configurator=SpringConfigurator.class, encoders = { CommonMessageEncoder.class, SystemMessageEncoder.class, HistoryMessageEncoder.class}) public class WebsocketServer { //Store the websocketServer instance and login name map corresponding to each client private static CopyOnWriteMap<WebsocketServer, String> webSocketUsernameMap = new CopyOnWriteMap<WebsocketServer, String>(); private MessageDao messageDao = (MessageDao)ContextLoader.getCurrentWebApplicationContext().getBean("messageDao"); // online member private static ConcurrentLinkedQueue<String> members = new ConcurrentLinkedQueue<String>(); //each websocket client talks to the server private Session session; public WebsocketServer() { } @OnOpen public void onOpen(Session session) { this.session = session; } @OnClose public void onClose() { String username = webSocketUsernameMap.get(this); removeMember(username); webSocketUsernameMap.remove(this); for (WebsocketServer webSocket : webSocketUsernameMap.keySet()) { try { sendMsg(webSocket, new SystemMessageResponse(MessageType.SYS_MSG, username, "exit", members)); } catch (Exception e) { e.printStackTrace (); } } } @OnMessage public void onMessage(String message, Session session) { JSONObject messageObject = new JSONObject(message); String type = messageObject.getString("messageType"); String content = messageObject.getString("message"); if (type.equals(MessageType.COM_MSG)) { // group message Date time = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (WebsocketServer item : webSocketUsernameMap.keySet()) { try { sendMsg(item, new CommonMessageResponse(MessageType.COM_MSG, sdf.format(time), webSocketUsernameMap.get(this), content)); } catch (Exception e) { e.printStackTrace (); } } ChatMessage msg = new ChatMessage(sdf.format(time), webSocketUsernameMap.get(this), content); messageDao.save(msg); } else if (type.equals(MessageType.SYS_MSG)) { //After the link is successful, the client will send the login name to record here webSocketUsernameMap.put(this, content); addMember(content); for (WebsocketServer webSocket : webSocketUsernameMap.keySet()) { try { sendMsg(webSocket, new SystemMessageResponse(MessageType.SYS_MSG, content, "enter", members)); } catch (Exception e) { e.printStackTrace (); } } } else { try { List<ChatMessage> historyMessage = messageDao.getList(); sendMsg(this, new HistoryMessageResponse(MessageType.HIS_MSG, historyMessage)); } catch (Exception e) { e.printStackTrace (); } } } @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } private void sendMsg(WebsocketServer webSocket, Object message) throws Exception { webSocket.session.getBasicRemote().sendObject(message); } public static int getMembersCount() { return members.size(); } public static void addMember(String member) { members.add(member); } public static void removeMember(String member) { members.remove(member); } }
renderings
Multiplayer, multichat room version
FEATURE
- Multi-person chat, multi-chat room, independent of each other, simple and beautiful interface, use ueditor to support sending text and picture information
- Group chat member list, log in and log out announcement
- Store chat history, view historical messages
technical point
- Use CopyOnWriteMap to store Session objects, thread-safe
- redis stores message records
- redis store chat members
@ServerEndpoint(value="/websocketServer/{chatRoomId}", configurator=SpringConfigurator.class, encoders = { CommonMessageEncoder.class, SystemMessageEncoder.class, HistoryMessageEncoder.class}) public class WebsocketServer { private ChatRoomDao chatRoomDao = (ChatRoomDao)ContextLoader.getCurrentWebApplicationContext().getBean("chatRoomDao"); private MessageDao messageDao = (MessageDao)ContextLoader.getCurrentWebApplicationContext().getBean("messageDao"); private ChatroomMemberDao chatroomMemberDao = (ChatroomMemberDao)ContextLoader.getCurrentWebApplicationContext().getBean("chatroomMemberDao"); // private Session session; public WebsocketServer() { } @OnOpen public void onOpen(Session session) { // this.session = session; } @OnClose public void onClose(Session session, @PathParam("chatRoomId") String chatRoomId) { String username = chatRoomDao.get(chatRoomId, session); chatroomMemberDao.remove(chatRoomId, username); chatRoomDao.remove(chatRoomId, session); for (Session sesion : chatRoomDao.getKeys(chatRoomId)) { try { sendMsg(sesion, new SystemMessageResponse(MessageType.SYS_MSG, username, "exit", chatroomMemberDao.getAll(chatRoomId))); } catch (Exception e) { e.printStackTrace (); } } } @OnMessage public void onMessage(String message, Session session, @PathParam("chatRoomId") String chatRoomId) { JSONObject messageObject = new JSONObject(message); String type = messageObject.getString("messageType"); String content = messageObject.getString("message"); if (type.equals(MessageType.COM_MSG)) { // group message Date time = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (Session sesion : chatRoomDao.getKeys(chatRoomId)) { try { sendMsg(sesion, new CommonMessageResponse(MessageType.COM_MSG, sdf.format(time), chatRoomDao.get(chatRoomId, session), content)); } catch (Exception e) { e.printStackTrace (); } } ChatMessage msg = new ChatMessage(sdf.format(time), chatRoomDao.get(chatRoomId, session), content); messageDao.save(chatRoomId, msg); } else if (type.equals(MessageType.SYS_MSG)) { chatRoomDao.save(chatRoomId, session, content); chatroomMemberDao.save(chatRoomId, content); for (Session sesion : chatRoomDao.getKeys(chatRoomId)) { try { sendMsg(sesion, new SystemMessageResponse(MessageType.SYS_MSG, content, "enter", chatroomMemberDao.getAll(chatRoomId))); } catch (Exception e) { e.printStackTrace (); } } } else { try { List<ChatMessage> historyMessage = messageDao.getList(chatRoomId); sendMsg(session, new HistoryMessageResponse(MessageType.HIS_MSG, historyMessage)); } catch (Exception e) { e.printStackTrace (); } } } @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } private void sendMsg(Session session, Object message) throws Exception { session.getBasicRemote().sendObject(message); } }renderings
one-on-one chat
technical point
- One-to-one relationship maintenance
- redis store chat history
TODU
- Close associated chats when offline
- achieve distributed
//Online friend list controller @ServerEndpoint(value="/websocketServer", configurator=SpringConfigurator.class, encoders = {SystemMessageEncoder.class, CommonMessageEncoder.class, HistoryMessageEncoder.class})//这边的session会被websocket中调用发送commonmessage,所以需要引入CommonMessageEncoder public class WebsocketServer { private ChatRoomDao chatRoomDao = (ChatRoomDao)ContextLoader.getCurrentWebApplicationContext().getBean("chatRoomDao"); private ChatroomMemberDao chatroomMemberDao = (ChatroomMemberDao)ContextLoader.getCurrentWebApplicationContext().getBean("chatroomMemberDao"); public WebsocketServer() { } @OnOpen public void onOpen(Session session) { } @OnClose public void onClose(Session session) { String username = chatRoomDao.getUname(session); chatroomMemberDao.remove(username); chatRoomDao.remove(session); for (Session sesion : chatRoomDao.getSessionKeys()) { try { sendMsg(sesion, new SystemMessageResponse(MessageType.SYS_MSG, username, "exit", chatroomMemberDao.getAll())); } catch (Exception e) { e.printStackTrace(); } } } @OnMessage public void onMessage(String message, Session session) { JSONObject messageObject = new JSONObject(message); String content = messageObject.getString("message"); String messageType = messageObject.getString("messageType"); if (messageType.equals(MessageType.SYS_MSG)) { chatRoomDao.save(session, content); chatroomMemberDao.save(content); for (Session sesion : chatRoomDao.getSessionKeys()) { try { sendMsg(sesion, new SystemMessageResponse(MessageType.SYS_MSG, content, "enter", chatroomMemberDao.getAll())); } catch (Exception e) { e.printStackTrace(); } } } } @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } private void sendMsg(Session session, Object message) throws Exception { session.getBasicRemote().sendObject(message); } }
//一对一聊天控制器 @ServerEndpoint(value="/chatServer", configurator=SpringConfigurator.class, encoders = {SystemMessageEncoder.class, CommonMessageEncoder.class, HistoryMessageEncoder.class}) public class ChatServer { private ChatRoomDao chatRoomDao = (ChatRoomDao)ContextLoader.getCurrentWebApplicationContext().getBean("chatRoomDao"); private ChatSessionStorageDao chatSessionStorageDao = (ChatSessionStorageDao)ContextLoader.getCurrentWebApplicationContext().getBean("chatSessionStorageDao"); private HistoryMessageDao historyMessageDao = (HistoryMessageDao)ContextLoader.getCurrentWebApplicationContext().getBean("historyMessageDao"); public ChatServer() { } @OnOpen public void onOpen(Session session) { } @OnClose public void onClose(Session session) { chatSessionStorageDao.closeSession(session); } @OnMessage public void onMessage(String message, Session session) { JSONObject messageObject = new JSONObject(message); String messageType = messageObject.getString("messageType"); String from = messageObject.getString("from"); String to = messageObject.getString("to"); if (messageType.equals(MessageType.SYS_MSG)) { chatSessionStorageDao.save(from, to, from, session); List<ChatMessage> historyMessages = historyMessageDao.get(from, to); if (historyMessages.size() > 0) { try { sendMsg(session, new HistoryMessageResponse(MessageType.HIS_MSG, historyMessages)); } catch (Exception e) { e.printStackTrace(); } } } else if (messageType.equals(MessageType.COM_MSG)) { String sendMessage = messageObject.getString("message"); //先从一对一session存储Map中查找session Session toSession = chatSessionStorageDao.getSession(from, to, to); if (toSession == null) { //没找到就从所有的session列表中查找 toSession = chatRoomDao.getSession(to); } if (toSession == null) { try { sendMsg(session, new SystemMessageResponse(MessageType.SYS_MSG, to, "", null)); } catch (Exception e) { e.printStackTrace(); } } else { Date time = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { historyMessageDao.save(from, to, new ChatMessage(sdf.format(time), from, sendMessage)); sendMsg(session, new CommonMessageResponse(MessageType.COM_MSG, sdf.format(time), from, sendMessage)); sendMsg(toSession, new CommonMessageResponse(MessageType.COM_MSG, sdf.format(time), from, sendMessage)); } catch (Exception e) { e.printStackTrace(); } } } } @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } private void sendMsg(Session session, Object message) throws Exception { session.getBasicRemote().sendObject(message); } }
效果图