1HOON
논리적 코딩
1HOON
전체 방문자
오늘
어제
  • HOME (186)
    • ☕️ JAVA (28)
      • WhiteShip Java LIVE Study (6)
      • Effective JAVA (10)
    • 🔮 KOTLIN (4)
    • 🌱 SPRING (51)
      • 스프링 인 액션 (22)
      • JPA (18)
    • ☕️ JAVASCRIPT (6)
    • 📃 DATABASE (40)
      • ORACLE (37)
      • MSSQL (2)
    • 🐧 LINUX (4)
    • 🐳 DOCKER (5)
    • 🐙 KUBERNETES (4)
    • 🏗️ ARCHITECTURE (8)
    • 📦 ETC (26)
      • TOY PROJECT (5)
      • RECRUIT (1)
      • 그냥 쓰고 싶어서요 (14)
    • 🤿 DEEP DIVE (1)
    • 🚽 Deprecated (9)
      • PYTHON (3)
      • AWS (2)
      • HTTP 완벽가이드 (3)
      • WEB (1)

블로그 메뉴

  • 홈
  • 방명록
  • 관리

인기 글

최근 글

티스토리

hELLO · Designed By 정상우.
1HOON

논리적 코딩

☕️ JAVA

WebSocketSession에서 HttpSession를 얻는 방법

2020. 9. 10. 23:54

개발 환경

  • JDK : Oracle JDK 1.8
  • WS : Apache 2.4
  • WAS : Apache Tomcat 9

웹 소켓을 이용해 채팅 프로그램을 만들었는데, 채팅 UI에서 사용자 이름을 표현하거나 채팅 로그를 남길 때 HttpSession에 저장해둔 로그인 사용자 정보를 이용하고 싶어서 구현해보았습니다.

 

우선 알아두어야할 것은 아래와 같습니다.

웹 소켓 세션과 서블릿 세션은 다릅니다. 때문에, 웹 소켓 세션에서 우리가 흔히 이야기하는 세션(HttpSession)에 접근할 수가 없습니다.

 

 

00. 사전 준비


우선 Apache에 웹 소켓 통신을 위한 세팅을 해주어야합니다.

아파치 설치경로/conf/httpd.conf 파일을 열어 아래 모듈들을 로드해주세요. 아마 대부분 작성이 되어있고 주석을 해제하시면 될겁니다.

 

1
2
3
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
cs

 

그리고 아파치 설치경로/conf/extra/httpd-vhosts.conf 파일을 열어 VirtualHost에 아래 내용을 추가해주세요.

만약 VirtualHost를 httpd.conf에 직접 작성하신경우 해당 위치에 추가해주시면 됩니다.

 

1
2
3
4
<VirtualHost *:80>
    ...
    ProxyPass /websocket/echo ws://127.0.0.1:8180/websocket/echo
</VirtualHost>
Colored by Color Scripter
cs

 

마지막으로, 저는 채팅 UI 서버를 담당하는 톰캣에서 웹 소켓 요청도 같이 처리하도록 하겠습니다.

톰캣의 server.xml 을 열어 8180 포트를 추가했습니다.

 

1
2
<Connector connectionTimeout="20000" port="8180" protocol="HTTP/1.1" redirectPort="8443"/>
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
cs

 

여기까지 설정을 해주셨다면 아래 과정을 거쳐 요청이 처리됩니다.

  1. 클라이언트단에서 ws://도메인:80/websocket/echo 요청
  2. Apache에서 해당 요청을 톰캣으로 Proxy 처리(8180)

 

 

01. Configurator 추가


 

아래 설정 클래스를 작성해 웹 소켓 통신이 시작되면, HttpSession을 해당 웹 소켓 통신에 저장해두겠습니다.

 

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
package logicalcode.websocket;
 
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;
 
/**
 * 웹 소켓 HTTP 세션 세팅 Configurator
 */
public class WebSocketSessionConfigurator extends Configurator 
{
   /**
    * 웹 소켓 세션의 UserProperties에 PRIVATE_HTTP_SESSION 으로 현재 HttpSession을 세팅해준다.
    */
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) 
    {       
       HttpSession session = (HttpSession) request.getHttpSession();
       
       if (session != null)
       {
          sec.getUserProperties().put("PRIVATE_HTTP_SESSION", session);
       }
    }
 
}
Colored by Color Scripter
cs

 

그리고 위 클래스의 HttpSession에서 Null이 취득되는 현상이 있어 아래 리스너를 추가했습니다.

 

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
package modular.core.listener;
 
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
 
/**
 * Servlet Request Listener
 */
@WebListener
public class RequestListener implements ServletRequestListener
{
 
   @Override
   public void requestDestroyed(ServletRequestEvent sre) 
   {
   }
 
   /**
    * WebSocketSessionConfigurator에서 HttpSession이 null 취득되어 오버라이드함.
    */
   @Override
   public void requestInitialized(ServletRequestEvent sre) 
   {
      ((HttpServletRequest) sre.getServletRequest()).getSession();
   }
   
}
Colored by Color Scripter
cs

 

 

02. 웹소켓 컨트롤러 작성


ws://도메인/websocket/main 요청을 처리하는 컨트롤러를 작성합니다. 이 컨트롤러에서는 위에서 작성한 설정을 적용해 Endpoint를 설정해줍니다.

 

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package logicalcode.websocket;
 
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
 
import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
 
@ServerEndpoint(value="/websocket/main", configurator=WebSocketSessionConfigurator.class)
public class MainEndpoint 
{
    private static final Logger logger = LoggerFactory.getLogger(MainEndpoint.class);
   
   /**
    * 현재 연결중인 세션 관리용 변수
    */
    private static final Map<Session, HttpSession> sessionMap = new HashMap<Session, HttpSession>();
   
   
   /**
    * 클라이언트가 정상적으로 웹 소켓 서버 접근 시 호출되는 메서드
    * @param session
    * @throws IOException 
    */
    @OnOpen
    public void onOpen (Session session, EndpointConfig config)
    {
        // HttpSession 객체 취득
        HttpSession httpSession = (HttpSession) config.getUserProperties().get("PRIVATE_HTTP_SESSION");
 
        logger.debug("WebSocket Session created : " + session.getId());
        logger.debug("Http Session is : " + httpSession.getId());
      
        sessionMap.put(session, httpSession);
    }
   
   /**
    * 클라이언트에서 메시지가 서버로 도착했을 때 호출되는 메서드
    * @param message
    * @param session
    * @throws IOException 
    */
    @OnMessage
    public void onMessage (String message, Session session)
    {
        HttpSession httpSession = sessionMap.get(session);
        logger.debug("Sender : " + httpSession.getId() + ", Message : " + message);
 
    }
   
    @OnError
    public void onError(Throwable e,Session session) 
    {
        e.printStackTrace();
    }
   
    @OnClose
    public void onClose(Session session)
    {
       HttpSession httpSession = sessionMap.get(session);
       
       logger.debug("WebSocket Session destroyed : " + session.getId());
       logger.debug("Http Session was : " + httpSession.getId());
      
       sessionMap.remove(session);
    }
}
Colored by Color Scripter
cs

 

 

03. JSP 작성


마지막으로 JSP 에서 웹 소켓 요청을 보내는 코드를 작성하면 끝입니다!

소스는 간단하게, body Element는 생략하고 body load 이벤트에서 웹 소켓 통신을 연결하도록 하겠습니다.

 

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
<html>
    <head>
        <script type="text/javascript">
            var mainWS;
 
            function body_load ()
            {
                if (mainWS !== undefined && mainWS.readyState !== WebSocket.CLOSED)
                {
                    console.log("WebSockey is already opened");
                    return;
                }
 
                mainWS = new WebSocket("ws://서버아이피주소:8180/websocket/main");
                
                mainWS.onopen = function (e)
                {
                    if (e.data === undefined)
                    {
                        return;
                    }
                }
            }
            
        </script>
    </head>
    <body>
    </body>
</html>
Colored by Color Scripter
cs

 

 

04. 테스트


작성한 JSP에 접속하게되면, 서버 로그에는 아래와 같이 로그가 남게됩니다.

웹 소켓 세션은 0부터 시작해서 순차적으로 증가하고 HttpSession의 아이디가 로그에 남는 것을 확인하실 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
2020-09-10 23:51:12,108 DEBUG [logicalcode.websocket.MainEndpoint] WebSocket Session created : 0
2020-09-10 23:51:12,108 DEBUG [logicalcode.websocket.MainEndpoint] WebSocket Session created : 0
2020-09-10 23:51:12,109 DEBUG [logicalcode.websocket.MainEndpoint] Http Session is : B1266BCCD78EA8F10A09AAEB7C61A9CB
2020-09-10 23:51:12,109 DEBUG [logicalcode.websocket.MainEndpoint] Http Session is : B1266BCCD78EA8F10A09AAEB7C61A9CB
 
 
2020-09-10 23:51:42,947 DEBUG [logicalcode.websocket.MainEndpoint] WebSocket Session created : 1
2020-09-10 23:51:42,947 DEBUG [logicalcode.websocket.MainEndpoint] WebSocket Session created : 1
2020-09-10 23:51:42,948 DEBUG [logicalcode.websocket.MainEndpoint] Http Session is : A74DFD0EC1A1ACFE8CECA11DF9FB4A45
2020-09-10 23:51:42,948 DEBUG [logicalcode.websocket.MainEndpoint] Http Session is : A74DFD0EC1A1ACFE8CECA11DF9FB4A45
cs

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'☕️ JAVA' 카테고리의 다른 글

BufferedReader 가 Scanner 보다 빠른 이유  (0) 2020.11.08
Enum을 사용해보자  (0) 2020.10.18
Spring AOP와 AspectJ 비교하기  (14) 2019.12.15
이미지를 BLOB 형태로 DB에 저장하기  (5) 2018.09.09
웹 서버와 웹 어플리케이션 서버의 차이  (1) 2017.12.22
    '☕️ JAVA' 카테고리의 다른 글
    • BufferedReader 가 Scanner 보다 빠른 이유
    • Enum을 사용해보자
    • Spring AOP와 AspectJ 비교하기
    • 이미지를 BLOB 형태로 DB에 저장하기
    1HOON
    1HOON

    티스토리툴바