다음은 전자정부프레임워크 3.5에서 웹소켓을 사용하기 위한 절차임
1. 요구사항
1) 전자정부프레임워크 : 3.5
2) 아파치 톰캣 : 7.0.70 이상(너무 버전이 낮아도 웹소켓을 지원안하므로 유의해야 함)
3) spring framework : 4.0.9.RELEASE
– 전자정부 프레임 3.5 버전에서 사용하는 스프링 버전, 더 높일수 있지만 다른 문제 발생할수 있음. 실제로 스프링 4.1에서는
그간 잘 써왔던 jackson의 지원이 중단되어 다른것으로 대체해야 하는등 문제가 발생한다.
4) JDK : 1.7 이상
5) SockJs : 익스플로러 낮은버전도 지원하기 위하여 사용
2. 메이븐 POM에 추가
4.0.9.RELEASE
org.springframework spring-context ${springframework.version} org.springframework spring-core ${springframework.version} org.springframework spring-beans ${springframework.version} org.springframework spring-web ${springframework.version} org.springframework spring-webmvc ${springframework.version} org.springframework spring-websocket ${springframework.version} javax.servlet javax.servlet-api 3.1.0 provided javax.websocket javax.websocket-api 1.0 provided
3. 웹소켓 설정
@Configuration @EnableWebMvc @EnableWebSocket public class WebSockConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry wshrRegistry) { // 웹소켓 wshrRegistry.addHandler(monHandler(), "/websocket/monHandler.ws") .addInterceptors(new MonHandshakeInterceptor()); // SockJs wshrRegistry.addHandler(monHandler(), "/websocket/monHandler.sockjs") .addInterceptors(new MonHandshakeInterceptor()) .withSockJS(); } @Bean public MonHandler monHandler() { return new MonHandler(); } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
4. DispatcherServletInitializer
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class>[] getRootConfigClasses() { return null; } @Override protected Class>[] getServletConfigClasses() { return new Class>[] { WebSockConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; //return new String[] {"*.do", "*.json"}; } @Override protected void customizeRegistration(Dynamic registration) { registration.setInitParameter("dispatchOptionsRequest", "true"); } }
5. 인터셉터
– 인터셉터를 쓴 이유는 사실 SockJs에서 넘긴 파라미터를 처리하기 위해서이다. 즉 아래와 같이 userId를 넘겼을때
받을수 있는곳은 인터셉터이기 때문이다.
wsSockJs = new SockJS("http://localhost:8080/websocket/monHandler.sockjs?userId=yomile");
public class MonHandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, MapmpAttributes) throws Exception { ServletServerHttpRequest sshRequest = (ServletServerHttpRequest)request; HttpServletRequest hsrRequest = sshRequest.getServletRequest(); String strUserId = hsrRequest.getParameter("userId"); mpAttributes.put("userId", strUserId); return super.beforeHandshake(request, response, wsHandler, mpAttributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { super.afterHandshake(request, response, wsHandler, ex); } }
6. 웹소켓 핸들러
– 위의 인테셉터에서 mpAttributes에 파라미터값을 다시 넣어주게 되면 아래 웹소켓 핸들러에서는 WebSocketSession의 getAttributes를
이용하여 값을 빼어 낼수 있게 된다. 결국 JavaScript의 Sockjs에서 넣은 파라미터 값이 최종적으로 여기까지 올수 있게 된다.
//@Component public class MonHandler extends TextWebSocketHandler { private static Logger mStatLogger = LoggerFactory.getLogger(MonHandler.class); private ListmLstSession = null; public MonHandler() { mLstSession = new ArrayList (); } @Override public void afterConnectionEstablished(WebSocketSession wssSession) throws Exception { Map mpParam = wssSession.getAttributes(); String strUserId = (String)mpParam.get("userId"); mStatLogger.debug(" 접속, UserId:"+ strUserId +",SessionId:"+ wssSession.getId() +", 연결 IP:" + wssSession.getRemoteAddress().getHostName() ); mLstSession.add(wssSession); } @Override protected void handleTextMessage(WebSocketSession wssCurSession, TextMessage tmMsg) throws Exception { mStatLogger.debug("*MonHandler.handleTextMessage"); mStatLogger.info("{}로 부터 {} 받음", wssCurSession.getId(), tmMsg.getPayload()); MonVO clsMonVO = MonVO.convert(tmMsg.getPayload()); mStatLogger.debug(" -clsMonVO.getTarget():"+ clsMonVO.getTarget()); mStatLogger.debug(" -clsMonVO.getMessage():"+ clsMonVO.getMessage()); Map mpCurParam = wssCurSession.getAttributes(); String strCurUserId = (String)mpCurParam.get("userId"); mStatLogger.debug(" - CurUserId:"+ strCurUserId); //연결된 모든 클라이언트에게 메시지 전송 for(WebSocketSession wssSession : mLstSession) { Map mpParam = wssSession.getAttributes(); String strUserId = (String)mpParam.get("userId"); if(clsMonVO.getTarget().equals(strUserId) == true) { // 전송대상한테만 메시지를 보낸다. wssSession.sendMessage(new TextMessage(clsMonVO.getMessage())); } } } @Override public void handleTransportError(WebSocketSession wssSession, Throwable exception) throws Exception { mStatLogger.debug("*MonHandler.handleTransportError"); if(wssSession.isOpen() == true) wssSession.close(); mLstSession.remove(wssSession); } @Override public void afterConnectionClosed(WebSocketSession wssSession, CloseStatus status) throws Exception { mStatLogger.debug("*MonHandler.afterConnectionClosed"); mStatLogger.info("{} 연결 끊김.", wssSession.getId()); mLstSession.remove(wssSession); } }
7. JSP 설정
<%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>test