

ws와 wss라는 URI를 사용한다.ws: 암호화되지 않은 연결에 사용. ws://와 같은 형식으로 작성하면 된다.wss: 암호화된 연결에 사용. TLS를 통과해 전달되므로 송신자측에서 데이터가 암호화, 수신자측에서 복호화된다. wss://와 같은 형식으로 작성하면 된다.웹소켓 연결을 위해서는 핸드쉐이크 요청을 전송하고 이에 대해 핸드쉐이크 응답(HTTP Status 101)를 받아야 한다.
101 Switching Protocol
서버가 전환되는 프로토콜을 가리킨다.
프로토콜은 클라이언트로부터 받은 Upgrade 헤더에 명시한다.
예시는 다음과 같다.
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
이렇게 TCP에 의존하는 HTTP 프로토콜로 웹소켓 연결을 위한 handshake를 수행하기 때문에, websocket 연결 후에도 HTTP 프로토콜 사용시에 TCP 연결로 생성된 socket을 그대로 사용한다..! (물론 이후로 HTTP 연결은 종료. 즉 HTTP 프로토콜을 더이상 사용하지 않는다.)
frame이라고 부르는 데이터 조각을 통해 이루어진다.
사실상 브라우저 환경에서는 text / binary data frame만 다루게 된다.
웹소켓 연결을 종료하는 과정도 hamndshake로 동작한다. 앞서 언급한 connection close frame을 클라이언트와 웹 서버 중 한쪽이 전송하면 handshake를 시작할 수 있다. frame에는 상태 코드와 이유가 포함될 수 있다. 다른 한 쪽이 프레임을 수신하면 마찬가지로 connection clsoe frame을 전송하여 응답해야 한다. connection close frame이 양방향으로 전송되고, 수신되면 websocket 연결은 종료된다.
참고로 웹소켓 연결이 종료되면, 아래 L4의 TCP 연결도 종료된다.(4-way handshaking)
Client Server
| |
|-- Close Frame ----->|
| |
|<-- Close Frame -----|
| |
|--- TCP FIN -------->|
| |
|<-- TCP FIN --------|
| |
@Configuration 어노테이션을 사용하는 클래스에 @EnableWebSocketMessageBroker을 추가하여 Spring에서 Websocket을 사용할 수 있다.
또 WebSocketMessageBrokerConfigurer 인터페이스를 구현하여 STOMP와 같은 simple messaging 프로토콜들을 다루기 위한 메세지를 생성하고 처리할 수 있다.
@Configuration
@RequiredArgsConstructor
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
private final StompHandler stompHandler;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint(("/chat"))
.setAllowedOriginPatterns("*")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(stompHandler); //stomp 메시지 핸들링
}
}