1. 웹소켓 이란?

→ 사용자의 브라우저와 서버 사이의 인터렉티브 통신 세션을 설정할 수 있게 하는 기술

  • Socket over HTTP (ws://... wss:///...) http상에 있다 ws란?웹소켓프로콜
  • Full duplex, 2-way communication
  • Polling, Long Pollling
  • Auto-reconnect with intelligence
  • 기본적으로 1:1 통신, 1: 전체 통신이 있다
  • room, channel을 이용하여 그룹통신 가능

 

[종류]

 

WebSocket

  • Socket Over HTTP
  • IE10+ (Can use pure WebSocket javascript library)

SockJS

  • Like socket.io(NodeJS) Polyfill (socket.io는 원래 nodejs기술)
  • IE 8+ (Use sockjs-client library)

STOMP(Straming Text Oriented Messaging Protocol) :토픽 구독방식

  • Spring Only, Publish(topic/queue) & Subscribe (topic이 1이면 1보는사람들)
  • Sub protocol Over SockJS → Custom Format for Message (sockjs사용?)
  • Use stopm js library!

 

[Ajax polling과 비교]

 

 

 

[Socket.readyState]

 

 

 

 

2. 기본 설정

 

(1) pom.xml

<dependency>추가

		<!-- WebSocket Start-->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-websocket</artifactId>
			<version>4.0.9.RELEASE</version>
		</dependency>
		
		<dependency>
			<groupId>javax.websocket</groupId>
			<artifactId>javax.websocket-api</artifactId>
			<version>1.1</version>
		</dependency>
		<!-- WebSocket End-->

 

 

 

(2) dispatcher-servlet.xml (servlet-context.xml) 에 bean 추가

xmlns:websocket="http://www.springframework.org/schema/websocket"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context-4.0.xsd
                http://www.springframework.org/schema/mvc 
                http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
                http://www.springframework.org/schema/tx 
                http://www.springframework.org/schema/tx/spring-tx.xsd
				http://www.springframework.org/schema/websocket
        		http://www.springframework.org/schema/websocket/spring-websocket.xsd">
                
<!--  websocket handler -->

    <websocket:handlers>
        <websocket:mapping handler="echoHandler" path="/echo.do" />
        <websocket:handshake-interceptors>
        	<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
        </websocket:handshake-interceptors>
    </websocket:handlers>

bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>

→ Http 세션 사용하기 위해 따로 추가해 줘야한다.

 

 

 

 

3. Class 만들기

(1) Java

-Make WebSocket Handler

  • extends TextWebSocketHandler(Text 전달 가능) or BinaryWebSockethandler(사진, 영상 등 미디어 전달 가능)
  • 여기선 Text만 주고 받을 거기 때문에 TextWebSockethandler를 이용했다.
package ercg.common.websocket;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import ercg.sys.log.cmm.UserDetailsHelper;
import ercg.sys.log.vo.SysLog0101VO;


public class EchoHandler extends TextWebSocketHandler{
	//연결된 모든 sessions 저장
	List<WebSocketSession> sessions = new ArrayList<>();
	//userId의 webSession을 저장한다
	Map<String, WebSocketSession> userSessions = new HashMap<>();
	
	//클라이언트 접속 성공 시 연결 성공시
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception{
		System.out.println("afterConnectionEstablished:" + session);
		
		//userId 알아내기
		Map<String, Object> sessionVal =  session.getAttributes();
		SysLog0101VO sysLog0101VO = (SysLog0101VO) sessionVal.get("sysLog0101VO"); 
		System.out.println(sysLog0101VO.getUserId());
		String userId = sysLog0101VO.getUserId();
		
		if(userSessions.get(userId) != null) {
			//userId에 원래 웹세션값이 저장되어 있다면 update
			userSessions.replace(userId, session);
		} else {
			//userId에 웹세션값이 없다면 put
			userSessions.put(userId, session);
		}
	}
	
	//소켓에 메시지를 보냈을때 js에서 on.message
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		System.out.println("handleTextmessage: " + session + " : " + message);
		
		//protocol: 내용, 보내는id, 받는id (content, requestId, responseId)
		String msg = message.getPayload();
		if(StringUtils.isNotEmpty(msg)) {
			String[] strs = msg.split(",");
			if (strs != null && strs.length == 3) {
				String sendId = strs[0];
				String receiveUserId = strs[1];
				String content = strs[2];
				
				//broadcasting
				if(receiveUserId.equals("")) {
					for (WebSocketSession sess: sessions) {
						//message를 TextMessage형태로 받음 (22번째줄, string x)
						sess.sendMessage(new TextMessage(receiveUserId + ":" + message.getPayload()));
					}
				} else {
					WebSocketSession responseIdSession = userSessions.get(receiveUserId);
					if (responseIdSession != null) {
						TextMessage tmpMsg = new TextMessage(sendId + "," + receiveUserId + "," + content);
						responseIdSession.sendMessage(tmpMsg);
					}
				}
				
			}
		}
		
	}

	
	//소켓이 close 됐을 때
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception{
		sessions.remove(session);
		System.out.println("afterHandleTextmessage: " + session + " : " + status);
	}
}

 

 

 

(2) JSP단

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<title>웹소켓 세션확인</title>
<%@ include file="/apps/common/include/ercgHeader.jsp"%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="/ercg/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript"
	src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.5/sockjs.min.js"></script>

<style type="text/css">
#messagePop {
	width: 437px;
	height: 256px;
	border: solid 1px;
	background-color: lightgrey;
}

#msg {
	width: 200px;
	height: 30px;
}

.well {
	margin-left: 5px;
    margin-top: 10px;
}

#receiveId {
	height: 30px;
	width: 90px;
}

.pop {
	width: 400px;
	height: 180px;
	border: 1px solid gray;
	position: relative;
}

#popTitle {
	height: 50px;
	background-color: #4D85C5;
}

#popTitle h3 {
	margin: 0;
	padding-top: 13px;
	margin-left: 10px;
	color: white;
}

#popCont {
	margin: 17px;
    border: 1px solid white;
    padding: 1em;
    
}

#popCont .pContent {
    margin-bottom: 1em;
    padding: 0;
}

.popClose {
	width: 1em;
	height: 1em;
	right: 0;
	top: 0;
	position: absolute;
	cursor: pointer;
	margin-right: 0.1em;
	margin-top: 0.1em;
	border: 1px solid white;
}
</style>
<script language="javascript">

var socket = null;
connect();

function connect() {
	var ws = new WebSocket("ws://localhost:8080/ercg/echo.do");
	socket = ws;
	//이벤트 헨들러
	ws.onopen = function() {
		console.log('Info: connection opened.');
	};
	
	//소켓에 메시지를 보냈을 때(sess.sendMessage) 여기서 받아짐 
	ws.onmessage = function (event) {
		var sm = event.data;
		var sl = sm.split(',');
		$("#writer").text("보내는 이 : " + sl[0]);
		$("#receiver").text("받는 이 : " + sl[1]);
		$("#content").text("내용: " + sl[2]);
		
		console.log("ReceiveMessage:" + event.data+'\n');
	};
	
	ws.onclose = function (event) { 
		console.log('Info: connection closed'); 
		//setTimeout( function() {connect(); }, 1000); // retry connection!!
	};
	
	ws.onerror = function (err) { console.log('Error:', err); };
}

$(document).ready(function() {
	$('#btnSend').on('click', function(evt) {
		let receiveId = $('#receiveId').val();
		let msg = $('#msg').val();
		evt.preventDefault();
		if (socket.readyState !== 1 ) return;
		socket.send(gUserId + "," + receiveId + "," + msg);
	});
	
	$('#wsClose').on('click', function(e) {
		socket.onclose();
		
	});
});

</script>
</head>
<body>
	<div id="messagePop">
		<div class="well">
		<span class="popClose"><p>x</p></span>
			<input type="text" id="receiveId" value="" class="form-control" /> <input
				type="text" id="msg" value="" class="form-control" />
			<button class="btn btn-primary" id="btnSend">Send Message</button>
		</div>
		
		<div id="popCont">
			<p id="writer" class="pContent"></p>
			<p id="receiver" class="pContent"></p>
			<p id="content" class="pContent"></p>
		</div>
	</div>
</body>
</html>

 

 

4. 결과 화면 출력

 

[기본 화면]

 

- 귀찮아서 label을 달진 않았지만 첫번째 칸이 보내려는 사람의 id, 두번째 칸이 메시지 내용이다.

 

 

 

(1) 연결 성공 시 웹F12 console창

 

 

 

 

(2) 메시지 출력

 

 

 

(3) eclipse console창

 

 

BELATED ARTICLES

more