본문 바로가기
CS

[CS] Web Socket

by doodoom 2022. 12. 13.

0. 이 글을 쓰게 된 이유

프로젝트를 진행하며 실시간 채팅 서버를 구축해야하는 요구 사항이 있었다. 그 과정에서 공부한 Web Socket에 대한 지식을 기록하고자 이 글을 쓰게 되었다.

1. HTTP 프로토콜의 한계

웹이 만들어진 초기에는 단순히 사용자가 리소스를 요청하면 응답하여서 보여주기만 하면 되었다. 그래서 HTTP 프로토콜은 요청을 보내면 응답을 받는 Request/Response 구조로 설계 되었고 이는 아주 짧은 시간 동안만 socket connection을 맺고 요청에 따른 응답을 보내주면 바로 끊어지게 된다. 즉, HTTP 프로토콜은 요청을 보내야 응답을 받는 단방향 통신(Simplex Communication) 방식이고(polling 같은 기술은 반이중방식), 이는 서버 쪽에서 아무리 데이터가 업데이트 되어도 클라이언트에서 다시 요청을 하지 않으면 데이터가 업데이트 되지 않는다는 것을 뜻한다.
하지만 시간이 지남에 따라 실시간으로 데이터가 업데이트 되어야하는 Real-time 통신 요구사항(채팅, 알림 등)이 생겼다. 이를 위해 HTTP 프로토콜은 polling, long polling, streaming 등의 기술을 활용해 해결해보려고 했지만 이는 근본적으로 Real-time 통신이라고 부르기 애매하거나, 서버의 리소스를 불필요하게 많이 사용하는 단점이 있다.(자세한 사항은 각 기술들을 공부해는 것이 좋다.)

2. Web Socket의 등장

위에서 소개한 HTTP 프로토콜의 한계를 극복하고자 생긴 것이 Web Socket이다. 즉, 웹 소켓은 HTTP와 구별되고, HTTP와 같이 OSI 모델의 7계층에 속하며, 4계층인 TCP에 의존한다. 또한 Web Socket은 현재 인터넷 환경(HTML5)에서 지원된다.
하지만 2011년 전의 브라우저에서는 지원이 되지 않지만 그 전 브라우저는 거의 쓰이지 않으니 크게 신경쓰지 않아도 될 것 같다.

3. Web Socket의 특징

  1. 양뱡향 통신(Full-Duplex)
    Web Socket은 HTTP 통신과 다르게 양방향 통신이다. 이는 데이터 송수신을 동시에 처리할 수 있음을 의미한다.
  2. 실시간 네트워킹 (Real-time networking)
    웹 환경에서 연속된 데이터를 빠르게 노출이 가능하다. 이는 채팅, 알림과 같은 요구 사항을 처리하기에 적합하다.
  3. Header의 간소화
    기존 HTTP 방식을 사용하면 요청을 보낼 때마다 Header에 필요한 정보를 담아서 보내야했다. 하지만 Web Socket은 최초에 Hand Shaking을 할 때에만 그 정도의 Header를 보내고 추후에는 최소한의 Header만을 실어서 보낼 수 있어 효율적이다.
  4. SSL 적용
    HTTPS와 마찬가지로 SSL을 사용하면 보안 수준을 높일 수 있다.
    HTTP -> ws (포트 80), HTTPS -> wss (포트 443)

4. Web Socket의 동작

Web Socket은 위와 같은 방식으로 작동하고 박스를 기준으로 나눌 수 있다.
크게 붉은 박스로 표시된 Opening Handshake와 노란 박스로 표시된 Data transfer, 보라색 박스로 표시된 Closing Handshake, 세 가지 영역으로 나눌 수 있다.

1. Hand Shaking

우리가 아는 핸드 셰이크의 개념에 protocol upgrade를 합친 방식이다.
클라이언트에서 핸드쉐이크 요청(HTTP)전송하면 서버는 메타 데이터를 확인하고 프로토콜을 websocket으로 업그레이드하고 적절한 응답을 보낸다. 클라이언트의 요청부터 살펴보자.

Request

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

Host나 Origin은 원래 Http 요청에서 기본으로 들어가는 것이니 다른 부분만 살펴보자.

  • Upgrade
    프로토콜을 전환하기 위해 사용하는 헤더. 웹소켓 요청 시에는 반드시 websocket이라는 값을 가지며, 이 값이 없거나 다른 값이면 cross-protocol attack이라고 간주하여 웹소켓 접속을 중지한다.
  • Connection
    현재의 전송이 완료된 후 네트워크 접속을 유지할 것인가에 대한 정보. 웹소켓 요청 시에는 반드시 Upgrade라는 값을 가지며, Upgrade와 마찬가지로 이 값이 없거나 다른 값이면 웹소켓 접속을 중지한다.
  • Sec-WebSocket-Key
    유효한 요청인지 확인하기 위해 사용하는 키 값. 임의로 선택된 16바이트 숫자를 base64로 인코딩 한 값이다.
    이를 통해 client와 서버 간의 신원을 인증 함.
  • Sec-WebSocket-Protocol
    사용하고자 하는 하나 이상의 웹 소켓 서브 프로토콜 지정.
    서버는 이 중에 사용 가능한 프로토콜을 응답.
  • Sec-WebSocket-Version
    클라이언트가 사용하고자 하는 웹소켓 프로토콜 버전. 현재 최신 버전 13

다른 헤더는 알아서 추가 가능하다.

Response

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
  • HTTP Status code는 101(Switching protocols)이다. 프로토콜을 성공적으로 바꿨다는 의미이다.
  • Upgrade와 Connection은 요청과 동일하다.
  • Sec-WebSocket-Accept
    요청 헤더의 Sec-WebSocket-Key에 유니크 아이디를 더해서 SHA-1로 해싱한 후, base64로 인코딩한 결과. 웹소켓 연결이 개시되었음을 알림.
  • Sec-WebSocket-Protocol
    요청 헤더의 Sec-WebSocket-Protocol 중 사용 가능한 프로토콜을 응답

2. Data Transfer

핸드쉐이크를 통해 웹소켓 연결이 수립되면, 데이터 전송 파트가 시작된다. 여기에서는 클라이언트와 서버가 '메시지'라는 개념으로 데이터를 주고받는데, 여기서 메시지는 한 개 이상의 '프레임'으로 구성되어 있다. (프레임은 텍스트(UTF-8) 데이터, 바이너리 데이터, 컨트롤 프레임(프로토콜 레벨의 신호) 등이 있다)

핸드 셰이크가 끝난 시점부터 서버와 클라이언트는 서로가 살아 있는지 확인하기 위해 heartbeat 패킷을 보내며, 주기적으로 ping을 보내 체크한다. 이는 서버와 클라이언트 양측에서 설정 가능하다.

3. Close Handshake

클라이언트와 서버 모두 커넥션을 종료하기 위한 컨트롤 프레임을 전송할 수 있다. 이 컨트롤 프레임은 Closing Handshake를 시작하라는 특정한 컨트롤 시퀀스를 포함한 데이터를 가지고 있다. 위 그림에서는 서버가 커넥션을 종료한다는 프레임을 보내고, 클라이언트가 이에 대한 응답으로 Close 프레임을 전송한다. 그러면 웹소켓 연결이 종료된다. 연결 종료 이후에 수신되는 모든 추가적인 데이터는 버려진다.