티스토리 뷰

Back-end/Spring

Spring WEB Socket

이안_ian 2019. 2. 24. 17:23




반응형

WebSocket은 Transport Protocol로써 웹에서 사용하는 Socket이다. HTTP에서 클라이언트와 서버는 연결을 유지하지 않는 ConnectionLess특징이 있다. Socket은 클라이언트와 서버가 연결을 유지하는 특성을 갖는데, WebSocket을 사용함으로써 웹에서 양방향 실시간 통신이 가능해진다. 

즉, 소켓으로 연결되어 있다면 클라이언트가 서버로 뭔가 요청하지 않더라도 서버가 보내고 싶을 때 클라이언트에게 데이터를 보낼 수 있다.


웹소켓은 RFC6445 표준이며, 프로토콜은 ws를 사용한다.

HTTP에서 WebSocket으로 포로토콜 전환을 WebSocket HandShake라고 한다. 브라우저는 프로토콜을 HTTP에서 WebSocket으로 전환하려는 요청을 Header에 Upgrade 속성을 추가하여 서버로 보낸다. 이 요청을 받은 서버가 WebSocket프로토콜을 이해하면 Upgrade 속성을 통해 프로토콜 전환을 동의하게 되고, 그러면 브라우저와 서버는 ws 프로토콜을 사용하게 된다.


ws 프로토콜로 전환이 되면, HTTP 연결은 중단되고 동일한 TCP/IP연결을 통해 WebSocket연결로 대체된다. WebSocket연결은 기본적으로 HTTP(80), HTTPS(443)와 동일한 포트를 사용한다. 즉 CORS적용이나 인증등의 과정을 기존 HTTP방식으로 사용할 수 있다는 장점


주의할점!! (Stateful)

Websocket은 HTTP와 달리 상태를 유지(Stateful)하기 때문에 서버와 클라이언트는 연결을 항상 유지해야 한다. 따라서 부하가 발생할 수 있다는 다점과 비정상적으로 연결이 끊어졌을 경우 적절하게 대응해야 한다.


출처:https://victorydntmd.tistory.com/250


웹소켓 설정하기


1. WebSocket 라이브러리 추가하기

1
2
3
4
5
6
7
8
9
10
11
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.5</version>
        </dependency>
cs

웹소켓을 사용하기 위한 websocket과 데이터 전송에 사용될 JSON을 위해 2개의 라이브러리를 추가한다.


2.websocket-config.xml 추가하기

servlet-context.xml에 네임스페이스로 가서 websocket을 추가해준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:websocket="http://www.springframework.org/schema/websocket"
    xsi:schemaLocation="http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.3.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
    
    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />
 
    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />
 
    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>
    
    <context:component-scan base-package="com.spring.ws" />
    
        <!-- 웹 소켓핸들러 -->
        <websocket:handlers>
               <websocket:mapping handler="echoHandler" path="/echo"/>
               <!-- WebSocket Handshake : 웹소켓 핸들러 클래스(echoHandler)가 호출되기전에 HttpSession에 접속하여  -->
               <!-- '이용자 아이디를 추출하는 기능' 등을 인터셉터가 수행하도록 설정 -->
               <websocket:handshake-interceptors>
                       <beans:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
               </websocket:handshake-interceptors>
               <!-- <websocket:sockjs /> -->
        </websocket:handlers>
        <beans:bean id="echoHandler" class="com.spring.util.EchoHandler" />
    
</beans:beans>
 
cs

다시 소스로 돌아와서 29번째 줄부터 아래를 추가해준다. 매핑값을 설정하여 그 경로로 들어오는 요청은 아래 com.spring.util.EchoHandler에서 처리를 하겠다는 의미다. 


async-supported는 1대 다 통신을 할때 필요한 비동기 설정이다.


3.Handler 객체 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.victolee.util;
 
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
 
public class EchoHandler extends TextWebSocketHandler {
    private List<WebSocketSession> sessionList = new ArrayList<WebSocketSession>();
     
      // 클라이언트와 연결 이후에 실행되는 메서드
      @Override
      public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessionList.add(session);
        System.out.println("{} 연결됨"+ session.getId());
      }
     
      // 클라이언트가 서버로 메시지를 전송했을 때 실행되는 메서드
      @Override
      protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println(("{}로 부터 {} 받음"+ session.getId()+ message.getPayload()));
        for (WebSocketSession sess : sessionList) {
          sess.sendMessage(new TextMessage(session.getId() + " : " + message.getPayload()));
        }
      }
     
      // 클라이언트와 연결을 끊었을 때 실행되는 메소드
      @Override
      public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessionList.remove(session);
        System.out.println(("{} 연결 끊김"+ session.getId()));
      }
}
 
cs

sessionList에는 들어오는 사람들의 세션들을 관리할 객체이고, 처음 /echo 매핑값으로 들어오면 처음 메소드가 실행되어 세션이 생성되고 List에 그걸 넣어줌 그 이후에 생성된 객체로 send 메소드를 호출하게되면 2번째 객체가 실행된다. 여기서 원하는 방향으로 메시지를 뿌려줄 수 있고 마지막 clsoe 메소드를 호출하면 연결이 끊어진다.


4.요청해줄 JSP 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>웹 소켓 통신</title>
</head>
<body>
    <div>
        <input type="text" id="messageinput">
    </div>
    
    <div>
        <button onclick="openSocket();">Open</button>
        <button onclick="send();">Send</button>
        <button onclick="closeSocket();">close</button>
    </div>
    
    <div id="message"></div>
    <script>
        var ws;
        var messages = document.getElementById("message");
        
        function openSocket(){
            if(ws!==undefined && ws.readyState!==WebSocket.CLOSED)
            {
                writeResponse("WebSocket is already opend.");
                return;
            } 
            
            //웹소켓 객체 만드는 코드
            ws = new WebSocket('ws://localhost:9090/ws/echo');
            
            ws.onopen=function(event){
                if(event.data===undefined) return;
                writeResponse(event.data);
            };
            ws.onmessage=function(event){
                writeResponse(event.data);
            };
            ws.onclose=function(event){
                writeResponse("Connection closed");
            }
        }
        function send(){
            var text = document.getElementById("messageinput").value;
            ws.send(text);
            text="";
        }
        function closeSocket(){
            ws.close();
        }
        function writeResponse(text){
            message.innerHTML+="<br/>"+text;
        }
    </script>
</body>
</html>
cs

아주 간단하게 웹소켓 생성하고 연결되면 메세지 보내고 닫기까지 가능한 상태로만 구현을 했다.

openSocket은 아까 지정했던 경로로 요청하게 하여 웹소켓을 생성하게하고 각각의 함수들을 매핑해준다.


결과창






반응형

'Back-end > Spring' 카테고리의 다른 글

AJAX 사용 시 데이터가 깨지는거 잡아주는 거  (0) 2019.03.07
Spring 파일 다운받기  (0) 2019.03.02
Spring 파일업로드 하기  (0) 2019.02.23
Spring MVC2 패턴으로 페이징 구성하기  (0) 2019.02.23
Spring AOP  (0) 2019.02.20
댓글
반응형
최근에 달린 댓글
글 보관함
Total
Today
Yesterday
최근에 올라온 글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31