Spring 4/WebSocket Multiuser chat
Spring 4 Multiuser Chat
Soul-Learner
2016. 5. 4. 19:06
Spring 4 WebSocket을 이용한 다중 이용자 채팅 예
채팅방에 접속한 이용자 중에서 특정 이용자를 선택하여 메시지를 전송할 수 있고 다른 이용자가 접속할 때마다 실시간에 채팅 중인 이용자의 화면에 이용자 리스트가 갱신된다
JSON 포맷으로 통신하고 서버측에서는 JSON문자열의 생성 및 파싱을 위해 JSON-Simple 라이브러리를 사용했다
이 코드는 완성된 채팅 시스템과는 거리가 멀고 스프링 4에서 제공하는 웹소켓 라이브러리를 단순하게 테스트하는 용도로 작성한 것이다
서블릿 설정파일의 웹소켓 관련 내용
<beans:bean id="chatHandler" class="org.kdea.websocket.ChatHandler"/> <websocket:handlers> <websocket:mapping handler="chatHandler" path="/chat-ws" /> <websocket:handshake-interceptors> <beans:bean class="org.kdea.interceptor.WebsocketHandshakeInterceptor"/> </websocket:handshake-interceptors> </websocket:handlers>
아래는 콘트롤러 클래스인데, 웹소켓을 사용할 때 스프링의 콘트롤러 클래스가 꼭 필요한 것은 아니지만 인터셉터를 사용하여 채팅 폼에 접속하는 이용자의 로그인 여부를 검사하려면 필요하다
ChatController.java
package org.kdea.java; import java.util.*; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping(value="/chat") public class ChatController { @RequestMapping(value="/form", method=RequestMethod.GET) public String form(){ return "/ws/chatform"; } }
WEB-INF/views/ws/chatform.jsp
JSON문자열을 이용하여 통신하며 서버로부터 메시지와 접속자 리스트를 수신하여 화면에 출력한다
<%@page import="java.util.List"%> <%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Websocket Client</title> <script type="text/javascript" src="<c:url value="/resources/jquery-2.2.2.min.js"/>"></script> <script type="text/javascript"> function connect() { var ws = new WebSocket("ws://192.168.8.32:8088/SpringWeb/chat-ws"); ws.onopen = function () { $('#chatStatus').text('Info: connection opened.'); $('input[name=chatInput]').on('keydown', function(evt){ if(evt.keyCode==13){ var userList = []; $('.receiver:checked').each(function(idx){ userList[idx] = $(this).val(); }); var msg = $('input[name=chatInput]').val(); var obj = {}; obj.receiver = userList; obj.msg = msg; var str = JSON.stringify(obj); ws.send(str); $('input[name=chatInput]').val(''); } }); alert('웹소켓 서버에 연결되었습니다'); }; ws.onmessage = function (event) { var obj = eval('('+event.data+')'); if('userList' in obj) { chatList(obj.userList); }else{ $('textarea').eq(0).prepend(obj.msg+'\n'); } }; ws.onclose = function (event) { $('#chatStatus').text('Info: connection closed.'); }; }; function chatList(userList) { $('#userList').empty(); for(var i=0;i<userList.length;i++) { $('#userList').append(userList[i]); var cb = $("<input type='checkbox' class='receiver' value='"+userList[i]+"' >"); $('#userList').append(cb).append('<br>'); } } </script> </head> <body> <p> <button type="button" onclick="connect();">서버 접속</button> <div id='chatStatus'></div> <textarea name="chatMsg" rows="5" cols="40"></textarea> <p> 메시지 입력 : <input type="text" name="chatInput"> <div id="userList"> </div> </body> </html>
ChatHandler.java
지정된 이용자에게만 메시지를 전달하고 새로 접속한 이용자가 있을 경우에는 모든 접속자에게 접속자 리스트를 새로 전달한다. 모든 통신에 JSON문자열을 이용한다
package org.kdea.websocket; import java.io.IOException; import java.util.*; import org.json.simple.*; import org.json.simple.parser.*; import org.springframework.web.socket.*; import org.springframework.web.socket.handler.TextWebSocketHandler; public class ChatHandler extends TextWebSocketHandler { static List<WebSocketSession> sessions = new ArrayList<>(); public ChatHandler(){ System.out.println("웹소켓 핸들러 생성됨"); } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("afterConnectionEstablished()"); sessions.add(session); informNewUser(); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { System.out.println("handleTextMessage()"); String sender = (String) session.getAttributes().get("userId"); String content = message.getPayload(); JSONParser jp = new JSONParser(); JSONObject jo = (JSONObject) jp.parse(content); String msg = (String)jo.get("msg"); msg = sender+":"+msg; JSONObject jsObj = new JSONObject(); jsObj.put("msg", msg); JSONArray ja = (JSONArray)jo.get("receiver"); for(int i=0;i<sessions.size();i++) { String uid = (String) sessions.get(i).getAttributes().get("userId"); for(int k=0;k<ja.size();k++) { String recvId = (String) ja.get(k); if(recvId.equals(uid)) { sessions.get(i).sendMessage(new TextMessage(jsObj.toJSONString())); } } } } private void informNewUser() { JSONArray ja = new JSONArray(); for(int i=0;i<sessions.size();i++) { String uid = (String) sessions.get(i).getAttributes().get("userId"); ja.add(uid); } JSONObject jo = new JSONObject(); jo.put("userList", ja); String jsonStr = jo.toJSONString(); try{ for(int i=0;i<sessions.size();i++) { sessions.get(i).sendMessage(new TextMessage(jsonStr)); } } catch (IOException e) { e.printStackTrace(); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { System.out.println("afterConnectionClosed()"); sessions.remove(session); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { System.out.println("handleTransportError()"); } }
WebSocketHandshakeInterceptor.java
package org.kdea.interceptor; import java.util.*; import javax.servlet.http.*; import org.springframework.http.server.*; import org.springframework.web.socket.*; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; public class WebsocketHandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { System.out.println("Before Handshake"); ServletServerHttpRequest ssreq = (ServletServerHttpRequest) request; HttpServletRequest req = ssreq.getServletRequest(); String id = (String)req.getSession().getAttribute("id"); attributes.put("userId", id); return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { System.out.println("After Handshake"); super.afterHandshake(request, response, wsHandler, ex); } }