• WEB_ Spring-WebSocket 통신 정의

    2021. 10. 6.

    by. KAEY

    1. 웹소켓

    Servlet 스택, 원시 WebSocket 상호 작용을 포함하는 WebSocket 메시징, SockJS를 통한 WebSocket 에뮬레이션, WebSocket을 통한 하위 프로토콜로 STOMP를 통한 게시-구독 메시징에 대한 지원을 다룹니다.

     

     

     

    1.1. 웹소켓 소개

    WebSocket 프로토콜인 RFC 6455 는 단일 TCP 연결을 통해 클라이언트와 서버 간에 전이중 양방향 통신 채널을 설정하는 표준화된 방법을 제공합니다. HTTP와 다른 TCP 프로토콜이지만 포트 80 및 443을 사용하고 기존 방화벽 규칙을 재사용할 수 있도록 HTTP를 통해 작동하도록 설계되었습니다.

    WebSocket 상호 작용은 HTTP Upgrade헤더를 사용하여 업그레이드하거나 이 경우 WebSocket 프로토콜로 전환 하는 HTTP 요청으로 시작됩니다 . 다음 예에서는 이러한 상호 작용을 보여줍니다.

     

    GET /spring-websocket-portfolio/portfolio HTTP/1.1
    Host: localhost:8080
    Upgrade: websocket  #
    Connection: Upgrade  #
    Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
    Sec-WebSocket-Protocol: v10.stomp, v11.stomp
    Sec-WebSocket-Version: 13
    Origin: http://localhost:8080

    #Upgrade헤더.

    #Upgrade연결을 사용합니다 .

     

     

    일반적인 200 상태 코드 대신 WebSocket을 지원하는 서버는 다음과 유사한 출력을 반환합니다.

    성공적인 핸드셰이크 후 HTTP 업그레이드 요청의 기반이 되는 TCP 소켓은 클라이언트와 서버 모두 계속해서 메시지를 보내고 받을 수 있도록 열려 있습니다.

    WebSocket의 작동 방식에 대한 완전한 소개는 이 문서의 범위를 벗어납니다. RFC 6455, HTML5의 WebSocket 장 또는 웹에 있는 많은 소개 및 자습서를 참조하십시오.

    WebSocket 서버가 웹 서버(예: nginx) 뒤에서 실행 중인 경우 WebSocket 업그레이드 요청을 WebSocket 서버로 전달하도록 구성해야 할 수 있습니다. 마찬가지로 애플리케이션이 클라우드 환경에서 실행되는 경우 WebSocket 지원과 관련된 클라우드 제공업체의 지침을 확인하십시오.

     

     

     

    1.2. HTTP 대 WebSocket

    WebSocket이 HTTP와 호환되도록 설계되고 HTTP 요청으로 시작하더라도 두 프로토콜이 매우 다른 아키텍처와 애플리케이션 프로그래밍 모델로 이어진다는 점을 이해하는 것이 중요합니다.

    HTTP 및 REST에서 애플리케이션은 많은 URL로 모델링됩니다. 애플리케이션과 상호 작용하기 위해 클라이언트는 요청-응답 스타일의 해당 URL에 액세스합니다. 서버는 HTTP URL, 메서드 및 헤더를 기반으로 요청을 적절한 처리기로 라우팅합니다.

    대조적으로 WebSocket에는 일반적으로 초기 연결을 위한 URL이 하나만 있습니다. 결과적으로 모든 애플리케이션 메시지는 동일한 TCP 연결에서 흐릅니다. 이것은 완전히 다른 비동기식 이벤트 기반 메시징 아키텍처를 나타냅니다.

    WebSocket은 HTTP와 달리 메시지 내용에 의미 체계를 규정하지 않는 저수준 전송 프로토콜이기도 합니다. 즉, 클라이언트와 서버가 메시지 의미 체계에 동의하지 않는 한 메시지를 라우팅하거나 처리할 방법이 없습니다.

    WebSocket 클라이언트와 서버는 Sec-WebSocket-ProtocolHTTP 핸드셰이크 요청 의 헤더를 통해 더 높은 수준의 메시징 프로토콜(예: STOMP) 사용을 협상할 수 있습니다 . 그것이 없다면 그들은 그들 자신의 관례를 마련해야 합니다.

     

     

    1.3. WebSocket을 사용해야 하는 경우

    WebSocket은 웹 페이지를 동적이고 대화식으로 만들 수 있습니다. 그러나 많은 경우 Ajax와 HTTP 스트리밍 또는 긴 폴링의 조합이 간단하고 효과적인 솔루션을 제공할 수 있습니다.

    예를 들어 뉴스, 메일 및 소셜 피드는 동적으로 업데이트되어야 하지만 몇 분마다 업데이트하는 것이 완벽할 수 있습니다. 반면에 협업, 게임 및 금융 앱은 실시간에 훨씬 더 가까워야 합니다.

    대기 시간만으로는 결정 요인이 아닙니다. 메시지 양이 비교적 적은 경우(예: 네트워크 오류 모니터링) HTTP 스트리밍 또는 폴링은 효과적인 솔루션을 제공할 수 있습니다. WebSocket을 사용하는 가장 좋은 사례는 낮은 대기 시간, 높은 빈도 및 높은 볼륨의 조합입니다.

    또한 인터넷을 통해 제어할 수 없는 제한적인 프록시는 Upgrade헤더 를 전달하도록 구성되지 않았 거나 유휴 상태로 나타나는 오래 지속되는 연결을 닫기 때문에 WebSocket 상호 작용을 방해할 수 있습니다 . 이는 방화벽 내에서 내부 응용 프로그램에 WebSocket을 사용하는 것이 공개 응용 프로그램에 사용하는 것보다 더 간단한 결정임을 의미합니다.

     

     

    2. 웹소켓 API

    Spring Framework는 WebSocket 메시지를 처리하는 클라이언트 및 서버 측 애플리케이션을 작성하는 데 사용할 수 있는 WebSocket API를 제공합니다.

     

     

    2.1. WebSocketHandler

    웹 소켓 서버를 만들기 구현 한 간단하다 WebSocketHandler또는 더 많은 가능성 중 하나를 확장 TextWebSocketHandler또는 BinaryWebSocketHandler. 다음 예제에서는 다음을 사용합니다 TextWebSocketHandler.

    import org.springframework.web.socket.WebSocketHandler;
    import org.springframework.web.socket.WebSocketSession;
    import org.springframework.web.socket.TextMessage;
    
    public class MyHandler extends TextWebSocketHandler {
    
        @Override
        public void handleTextMessage(WebSocketSession session, TextMessage message) {
            // ...
        }
    
    }

     

     

    다음 예제와 같이 이전 WebSocket 핸들러를 특정 URL에 매핑하기 위한 전용 WebSocket Java 구성 및 XML 네임스페이스 지원이 있습니다.

    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    
    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(myHandler(), "/myHandler");
        }
    
        @Bean
        public WebSocketHandler myHandler() {
            return new MyHandler();
        }
    
    }

     

     

     

    다음 예는 앞의 예와 동일한 XML 구성을 보여줍니다.

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:websocket="http://www.springframework.org/schema/websocket"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/websocket
            https://www.springframework.org/schema/websocket/spring-websocket.xsd">
    
        <websocket:handlers>
            <websocket:mapping path="/myHandler" handler="myHandler"/>
        </websocket:handlers>
    
        <bean id="myHandler" class="org.springframework.samples.MyHandler"/>
    
    </beans>

    앞의 예제는 Spring MVC 애플리케이션에서 사용하기 위한 것이며 DispatcherServlet. 그러나 Spring의 WebSocket 지원은 Spring MVC에 의존하지 않습니다. WebSocketHandler의 도움으로 다른 HTTP 제공 환경 에 통합하는 것은 비교적 간단합니다 WebSocketHttpRequestHandler.

    WebSocketHandlerAPI를 직접 또는 간접적으로 사용할 때( 예: STOMP 메시징을 통해) 응용 프로그램은 기본 표준 WebSocket 세션(JSR-356)이 동시 전송을 허용하지 않기 때문에 메시지 전송을 동기화해야 합니다. 한 가지 옵션은 로 래핑하는 것 WebSocketSession입니다 ConcurrentWebSocketSessionDecorator.

     

     

     

    2.2. 웹소켓 핸드셰이크

    초기 HTTP WebSocket 핸드셰이크 요청을 사용자 지정하는 가장 쉬운 방법은 핸드셰이크 HandshakeInterceptor"전" 및 "후"에 대한 메서드를 노출하는 를 사용하는 것입니다. 이러한 인터셉터를 사용하여 핸드셰이크를 방지하거나 모든 속성을 WebSocketSession. 다음 예제에서는 내장 인터셉터를 사용하여 HTTP 세션 속성을 WebSocket 세션에 전달합니다.

    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(new MyHandler(), "/myHandler")
                .addInterceptors(new HttpSessionHandshakeInterceptor());
        }
    
    }

     

     

    다음 예는 앞의 예와 동일한 XML 구성을 보여줍니다.

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:websocket="http://www.springframework.org/schema/websocket"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/websocket
            https://www.springframework.org/schema/websocket/spring-websocket.xsd">
    
        <websocket:handlers>
            <websocket:mapping path="/myHandler" handler="myHandler"/>
            <websocket:handshake-interceptors>
                <bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
            </websocket:handshake-interceptors>
        </websocket:handlers>
    
        <bean id="myHandler" class="org.springframework.samples.MyHandler"/>
    
    </beans>

    더 고급 옵션은 DefaultHandshakeHandler클라이언트 출처 확인, 하위 프로토콜 협상 및 기타 세부 정보를 포함하여 WebSocket 핸드셰이크 단계를 수행하는 확장 입니다. 애플리케이션은 RequestUpgradeStrategy아직 지원되지 않는 WebSocket 서버 엔진 및 버전에 적응하기 위해 사용자 지정을 구성해야 하는 경우 이 옵션을 사용해야 할 수도 있습니다 ( 이 주제에 대한 자세한 내용은 배포 참조 ). Java 구성과 XML 네임스페이스 모두 사용자 정의 HandshakeHandler.

     

     

     

    2.3. 전개

    Spring WebSocket API는 DispatcherServletHTTP WebSocket 핸드셰이크 및 기타 HTTP 요청을 모두 제공 하는 Spring MVC 애플리케이션에 쉽게 통합됩니다 . 또한 을(를) 호출하여 다른 HTTP 처리 시나리오에 쉽게 통합할 수 WebSocketHttpRequestHandler있습니다. 이것은 편리하고 이해하기 쉽습니다. 그러나 JSR-356 런타임과 관련하여 특별한 고려 사항이 적용됩니다.

    Java WebSocket API(JSR-356)는 두 가지 배포 메커니즘을 제공합니다. 첫 번째는 시작 시 서블릿 컨테이너 클래스 경로 스캔(서블릿 3 기능)을 포함합니다. 다른 하나는 Servlet 컨테이너 초기화 시 사용할 등록 API입니다. 이러한 메커니즘 중 어느 것도 Spring MVC와 같은 WebSocket 핸드셰이크 및 기타 모든 HTTP 요청을 포함한 모든 HTTP 처리에 대해 단일 "프론트 컨트롤러"를 사용할 수 없도록 합니다 DispatcherServlet.

    이것은 Spring의 WebSocket 지원 RequestUpgradeStrategy이 JSR-356 런타임에서 실행되는 경우에도 서버별 구현으로 주소를 지정하는 JSR-356의 중요한 제한 사항입니다 . 이러한 전략은 현재 Tomcat, Jetty, GlassFish, WebLogic, WebSphere 및 Undertow(및 WildFly)에 대해 존재합니다.

     

     

     

    두 번째 고려 사항은 JSR-356을 지원하는 서블릿 컨테이너가 ServletContainerInitializer응용 프로그램 시작을 느리게 할 수 있는 (SCI) 스캔 을 수행할 것으로 예상 된다는 것입니다. JSR-356을 지원하는 Servlet 컨테이너 버전으로 업그레이드한 후 상당한 영향이 관찰 되면 다음 예제와 같이 의 <absolute-ordering />요소를 사용하여 웹 조각(및 SCI 스캔)을 선택적으로 활성화 또는 비활성화할 수 있어야 합니다 web.xml.

    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
            http://java.sun.com/xml/ns/javaee
            https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
    
        <absolute-ordering/>
    
    </web-app>

     

     

     

    런 다음 SpringServletContainerInitializerServlet 3 Java 초기화 API에 대한 지원을 제공하는 Spring 자체와 같이 이름별로 웹 조각을 선택적으로 활성화할 수 있습니다 . 다음 예에서는 그렇게 하는 방법을 보여줍니다.

    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
            http://java.sun.com/xml/ns/javaee
            https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
    
        <absolute-ordering>
            <name>spring_web</name>
        </absolute-ordering>
    
    </web-app>

     

     

     

    3. 서버 구성

    각 기본 WebSocket 엔진은 메시지 버퍼 크기, 유휴 시간 제한 등과 같은 런타임 특성을 제어하는 ​​구성 속성을 노출합니다. Tomcat, WildFly 및 GlassFish의 ServletServerContainerFactoryBean경우 다음 예제와 같이 WebSocket Java 구성에 a 를 추가할 수 있습니다 .

    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Bean
        public ServletServerContainerFactoryBean createWebSocketContainer() {
            ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
            container.setMaxTextMessageBufferSize(8192);
            container.setMaxBinaryMessageBufferSize(8192);
            return container;
        }
    
    }

     

     

    다음 예는 앞의 예와 동일한 XML 구성을 보여줍니다.

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:websocket="http://www.springframework.org/schema/websocket"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/websocket
            https://www.springframework.org/schema/websocket/spring-websocket.xsd">
    
        <bean class="org.springframework...ServletServerContainerFactoryBean">
            <property name="maxTextMessageBufferSize" value="8192"/>
            <property name="maxBinaryMessageBufferSize" value="8192"/>
        </bean>
    
    </beans>

     

     

     

    Jetty의 경우 미리 구성된 Jetty를 제공 WebSocketServerFactory하고 DefaultHandshakeHandlerWebSocket Java 구성을 통해 Spring에 연결해야 합니다. 다음 예에서는 그렇게 하는 방법을 보여줍니다.

    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(echoWebSocketHandler(),
                "/echo").setHandshakeHandler(handshakeHandler());
        }
    
        @Bean
        public DefaultHandshakeHandler handshakeHandler() {
    
            WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
            policy.setInputBufferSize(8192);
            policy.setIdleTimeout(600000);
    
            return new DefaultHandshakeHandler(
                    new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
        }
    
    }

     

     

     

    다음 예는 앞의 예와 동일한 XML 구성을 보여줍니다.

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:websocket="http://www.springframework.org/schema/websocket"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/websocket
            https://www.springframework.org/schema/websocket/spring-websocket.xsd">
    
        <websocket:handlers>
            <websocket:mapping path="/echo" handler="echoHandler"/>
            <websocket:handshake-handler ref="handshakeHandler"/>
        </websocket:handlers>
    
        <bean id="handshakeHandler" class="org.springframework...DefaultHandshakeHandler">
            <constructor-arg ref="upgradeStrategy"/>
        </bean>
    
        <bean id="upgradeStrategy" class="org.springframework...JettyRequestUpgradeStrategy">
            <constructor-arg ref="serverFactory"/>
        </bean>
    
        <bean id="serverFactory" class="org.eclipse.jetty...WebSocketServerFactory">
            <constructor-arg>
                <bean class="org.eclipse.jetty...WebSocketPolicy">
                    <constructor-arg value="SERVER"/>
                    <property name="inputBufferSize" value="8092"/>
                    <property name="idleTimeout" value="600000"/>
                </bean>
            </constructor-arg>
        </bean>
    
    </beans>

     

     

     

    4. 허용된 출처

    Spring Framework 4.1.5부터 WebSocket 및 SockJS의 기본 동작은 동일한 출처 요청만 수락하는 것입니다. 전체 또는 지정된 출처 목록을 허용하는 것도 가능합니다. 이 검사는 대부분 브라우저 클라이언트용으로 설계되었습니다. 다른 유형의 클라이언트가 Origin헤더 값 을 수정하는 것을 막는 것은 없습니다 (자세한 내용은 RFC 6454: Web Origin Concept 참조).

     

     

     

    세 가지 가능한 동작은 다음과 같습니다.

    • 동일한 출처 요청만 허용(기본값): 이 모드에서 SockJS가 활성화되면 Iframe HTTP 응답 헤더 X-Frame-Options가 로 설정 SAMEORIGIN되고 JSONP 전송은 요청 출처 확인을 허용하지 않기 때문에 비활성화됩니다. 따라서 이 모드가 활성화되면 IE6 및 IE7이 지원되지 않습니다.
    • 지정된 출처 목록 허용: 허용되는 각 출처는 http:// 또는 로 시작해야 합니다 https://. 이 모드에서 SockJS가 활성화되면 IFrame 전송이 비활성화됩니다. 따라서 이 모드가 활성화되면 IE6 ~ IE9는 지원되지 않습니다.
    • 모든 출처 허용: 이 모드를 활성화하려면 *허용된 출처 값으로 제공해야 합니다 . 이 모드에서는 모든 전송을 사용할 수 있습니다.

     

     

    다음 예제와 같이 WebSocket 및 SockJS 허용 출처를 구성할 수 있습니다.

    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    
    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com");
        }
    
        @Bean
        public WebSocketHandler myHandler() {
            return new MyHandler();
        }
    
    }

     

     

     

    다음 예는 앞의 예와 동일한 XML 구성을 보여줍니다.

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:websocket="http://www.springframework.org/schema/websocket"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/websocket
            https://www.springframework.org/schema/websocket/spring-websocket.xsd">
    
        <websocket:handlers allowed-origins="https://mydomain.com">
            <websocket:mapping path="/myHandler" handler="myHandler" />
        </websocket:handlers>
    
        <bean id="myHandler" class="org.springframework.samples.MyHandler"/>
    
    </beans>

     


    댓글 (비로그인 댓글 허용하지 않습니다.)