이 포스트에서는 웹소켓에 대해 알아보는 시간을 가지도록 한다.
웹소켓(WebSocket)이란?
웹소켓은 RFC 6455에 의해 표준화가 된 통신 프로토콜이며 서버와 클라이언트가 실시간 양방향 통신을 할 수 있도록 하는 것이 목표이다.
웹소켓이 생기기 전 HTTP로 통신하는 클라이언트가 서버가 실시간으로 통신을 하기 위해서는, 혹은 그렇게 보이도록 하기 위해서는 Server-Sent Events, 롱 폴링, XHR과 같은 기법을 사용했으나 HTTP의 요청/응답에 붙는 헤더 크기로 인한 오버헤드가 크다는 문제가 있었다.
웹소켓은 단일 TCP 연결을 수립하여 네트워크 오버헤드를 줄이고 양방향 통신을 가능하게 해 준다.
이에 대해 벤치마크를 진행한 자료가 있다.
https://www.codeproject.com/Articles/209041/HTML5-Web-Socket-in-Essence#Background
HTML5 Web Socket in Essence
HTML5 WebSocket defines a bi-directional, full-duplex communication channel operates through a single TCP connection, this article discusses its fantastic performance, the WebSocket protocol principle and its handshake mechanism, and develop a WebSocket ap
www.codeproject.com
그림만 봐도 웹소켓이 훨씬 빠르다는 것을 알 수 있다.
이런 이유로 그냥 구현된 프로토콜을 사용할 수 있으면서 속도도 적당히 빠른 클라이언트-서버 간 실시간 통신을 위해 사용된다. 웹 브라우저뿐만 아니라 웹소켓 프로토콜을 지원한다면 어떤 것이든 사용할 수 있다.
웹소켓의 연결 과정
웹소켓 연결을 하고자 하는 클라이언트는 먼저 서버로 헤더가 Upgrade인 HTTP(1.1 이상) 요청을 보낸다.
요청을 받은 서버는 연결을 수락한다는 의미로 101 Switching Protocols를 응답하여 핸드셰이크가 끝나고 실시간 통신을 할 수 있게 된다.
웹소켓의 메시지 구조
웹소켓은 프레임 기반 구조를 가지고 있어 메시지를 전송할 때는 여러 개의 프레임으로 나뉘어 전송된다.
메시지로 보낼 수 있는 데이터의 종류는 텍스트와 바이너리 데이터가 있으며 핑퐁과 같은 제어 프레임도 포함된다.
웹소켓 메시지가 가지는 주요 필드는 다음과 같다.
- FIN 비트: 메시지의 끝
- opcode: 메시지 유형을 나타낸다.
- MASK: 웹소켓 메시지는 cache poisoning 방지를 위해 마스킹을 적용한다. 클라이언트와 서버 간 보안을 강화하는 XOR 마스킹이 사용된다.
- payload: 실제 전송할 데이터
웹소켓의 연결 종료
웹소켓의 연결 종료 역시 핸드셰이크 과정으로 이뤄진다.
클라이언트나 서버가 상대방에게 Close 프레임을 전송하고 상대방이 다시 Close를 보내면 연결이 정상적으로 종료된다.
하지만 웹소켓의 연결이 비정상적으로 종료되면 재연결을 구현할 필요가 있다.
웹소켓의 Ping/Pong
연결된 웹소켓의 경우에는 연결이 유지되고 있는지 확인을 위해 ping 프레임을 보내고 pong 프레임으로 응답하는 절차가 있다.
프레임이 제대로 전달되지 않으면 연결에 문제가 생긴 것으로 판단하게 된다.
URI
웹소켓의 URI는 ws와 wss를 쓴다. ws에 TLS이 적용된 것이 wss이며 기본으로 사용하는 포트는 각각 80과 443이다.
TCP 소켓과의 차이점?
웹소켓이나 TCP 소켓이나 어쨌든 TCP로 통신한다는 점은 같지만 다음과 같은 차이점이 있다.
프레임 구조
웹소켓은 정해진 프레임 구조를 따르지만 TCP 소켓은 패킷 구조가 정해져 있지 않으므로 필요에 따라 적절한 구조를 짜야한다. 그래서 웹소켓은 메시지를 프레임 단위로 전송하지만 TCP 소켓은 바이트 스트림 기반으로 자유롭게 메시지를 전달할 수 있다.
보안
웹소켓은 wss로 연결하면 TLS가 적용되지만 TCP 소켓은 기본적으로 붙는 암호화가 없기 때문에 필요하다면 별도로 적용해야 한다.
웹소켓을 지원하는 언어나 라이브러리
웹소켓은 많은 라이브러리에서 다양한 형태로 쓸 수 있다.
파이썬의 FastAPI, golang의 웹소켓 모듈, 자바 스프링의 STOMP(웹소켓을 한 단계 더 추상화 한 프로토콜) 등등등 그냥 웬만한 프로그래밍 언어에서 사용할 수 있는 방법이 있다.