[Spring/Web Socket] 웹 소켓 세션을 이용한 웹상에서 통신 (1)
2021. 3. 19. 10:57
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창
'Web > spring' 카테고리의 다른 글
[Websocket] 웹 소켓 세션을 이용한 웹상에서 통신 (3) - RabbitMQ 띄우기 (0) | 2021.04.01 |
---|---|
[Spring/Web Socket] 웹 소켓 세션을 이용한 웹상에서 통신 (2) - 단체톡방 (2) | 2021.03.24 |
[sqlMap] There is no statement named / sqlMap.xml 오류 (0) | 2021.03.02 |
[Spring] No mapping found for HTTP request with URI (0) | 2021.01.15 |
[Spring/JAVA] 사용자 IP, 접속자IP, 클라이언트IP 주소 가져오기 (0) | 2021.01.13 |