네트워크

WebSocket

계양 꿀주먹 2024. 8. 19. 20:53

WebSocket 이전의 이벤트 통신

사용자와 웹 서버는 HTTP 요청 - 응답 방식으로 통신합니다.

하지만 서버에서 클라이언트에게 응답을 해야할 때가 있는데 채팅, 알람과 같이 이벤트가 발생했을 때 사용자의 요청이 없더라도 사용자에게 단방향 응답을 해야했습니다.

웹소켓이 나오기 이전에는 Polling, Long Polling 등의 방식을 이용해서 해결했습니다.

 

 

1. Polling

사용자가 웹 서버에 주기적으로 요청을 보내 이벤트 내용을 확인하는 방식입니다.

구현하기 쉽지만 이벤트가 발생하더라도 바로 확인할 수 없으며 사용자가 많아지게 되면 많은 양의 요청이 서버로 보내지기 때문에 서버에 대한 부담이 커지게 됩니다.

 

2. Long Polling

사용자가 웹 서버로 요청을 보내지만, 서버에서는 바로 응답하지 않고 이벤트가 발생했을 때 웹 서버가 응답하고 클라이언트는 응답을 받으면 다시 웹 서버에 요청을 보냅니다.

Polling 에 비해 이벤트가 발생했을 때 바로 응답을 받을 수 있고 주기적인 요청이 없기 때문에 요청량이 줄어들지만, 이벤트가 발생할 때까지 응답을 대기해야 하기 때문에 서버의 자원을 낭비하게 되며 이벤트가 계속 발생하게 된다면 여전히 많은 양의 요청이 발생하게 됩니다.

 

3. Streaming

사용자가 웹 서버로 스트리밍 요청을 보내고 웹 서버에서 연결을 끊지 않고 지속적으로 응답합니다.

이벤트가 자주 발생하는 환경이나 실시간 데이터 업데이트, 라이브 스트리밍 같은 환경에서 사용합니다.

 

이 방식들은 모두 HTTP를 통해 통신하기 때문에 Header가 불필요하게 크다는 단점이 있습니다.


WebSocket

이전의 통신 방식처럼 사용자가 요청을 보내야지만 응답할 수 있는 것이 아닌 서버에서도 데이터를 보낼 수 있는 양방향 통신을 지원하기 위해 등장한 프로토콜입니다. 

HTTP 기반으로한 프로토콜이기 기존의 통신처럼 HTTP / HTTPS 요청을 보내고 Handshake 과정을 통해 WebSocket 프로토콜로 업그레이드 됩니다.

 

- WebSocket 통신 과정

 

* HandShake 과정

- 요청

일반적인 HTTP 요청과 비슷하지만 특별한 헤더가 포함되어 있습니다.

 

  • Upgrade: websocket: 이 헤더는 클라이언트가 HTTP에서 WebSocket으로 프로토콜을 업그레이드하려고 한다는 것을 서버에 알립니다.
  • Connection: Upgrade: 이 헤더는 연결을 업그레이드하려는 의도를 나타냅니다.
  • Sec-WebSocket-Key: 클라이언트가 임의로 생성한 고유한 키로, 서버가 이 키를 이용해 응답을 검증하게 됩니다.
  • Sec-WebSocket-Version: 클라이언트가 지원하는 WebSocket 프로토콜의 버전을 나타냅니다.

 

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: aaaaaaaaaa
Sec-WebSocket-Version: 13

 

- 응답

  • 서버는 클라이언트의 요청을 받고, WebSocket 업그레이드 요청을 처리합니다.
  • 서버가 WebSocket 연결을 허용할 경우, 101 Switching Protocols라는 상태 코드로 응답합니다. 이는 클라이언트와의 통신 프로토콜이 HTTP에서 WebSocket으로 변경되었음을 의미합니다.
  • 서버는 Sec-WebSocket-Accept 헤더에 클라이언트의 Sec-WebSocket-Key를 기반으로 생성된 값을 포함하여 응답합니다. 이 값은 클라이언트가 응답의 유효성을 확인하는 데 사용됩니다.
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: bbbbbbbbbbb

 

이렇게 HandShake가 완료되면 ws 혹은 wss 프로토콜로 변경되고 실시간 데이터 통신이 가능한 상태로 변경됩니다.

 

- WebSocket Frame

웹 소켓에서는 frame 단위로 데이터를 주고 받습니다.

frame 구조

 

- Frame 구조

  1. FIN(1 bit):
    • 메시지의 마지막 프레임인지 여부를 나타냅니다.
    • 1이면 이 프레임이 메시지의 마지막 부분을 의미하고, 0이면 이후에 더 많은 프레임이 이어짐을 의미합니다.
  2. RSV1, RSV2, RSV3(각각 1 bit):
    • 예약된 비트로, 기본적으로 0으로 설정되어야 합니다. 특정 확장이나 프로토콜에 따라 다른 목적으로 사용될 수 있습니다.
  3. Opcode(4 bits):
    • 이 필드는 프레임의 유형을 정의합니다.
    • 일반적으로 사용되는 Opcode 값은 다음과 같습니다:
      • 0x0: 계속되는 프레임 (Continuation Frame)
      • 0x1: 텍스트 프레임 (Text Frame)
      • 0x2: 바이너리 프레임 (Binary Frame)
      • 0x8: 연결 종료 (Connection Close)
      • 0x9: 핑 (Ping)
      • 0xA: 퐁 (Pong)
  4. Mask(1 bit):
    • 클라이언트가 서버로 보내는 모든 프레임에는 이 비트가 1로 설정되고, 데이터가 마스킹됩니다.
    • 서버에서 클라이언트로 보내는 프레임은 마스킹하지 않으며, 이 비트는 0으로 설정됩니다.
  5. Payload Length(7 bits, 7+16 bits, or 7+64 bits):
    • 이 필드는 페이로드 데이터의 길이를 나타냅니다.
    • 값이 0-125 사이인 경우, 해당 숫자가 페이로드의 길이를 직접 나타냅니다.
    • 값이 126인 경우, 다음 2바이트(16비트)가 실제 페이로드 길이를 나타냅니다.
    • 값이 127인 경우, 다음 8바이트(64비트)가 실제 페이로드 길이를 나타냅니다.
  6. Masking Key(32 bits, 선택 사항):
    • 마스크 비트가 1로 설정된 경우, 이 필드에는 4바이트의 마스킹 키가 포함됩니다.
    • 페이로드 데이터는 이 마스킹 키를 사용해 XOR 연산을 통해 마스킹됩니다. 클라이언트가 서버로 데이터를 보낼 때 사용됩니다.
  7. Payload Data:
    • 실제 데이터가 이 필드에 포함됩니다. 이 데이터는 텍스트, 바이너리, 제어 프레임 등에 따라 다를 수 있습니다.

- 동작 방식

 

  • 메시지의 조립: WebSocket 프레임은 메시지를 조립하는 데 사용됩니다. 큰 메시지는 여러 프레임으로 나누어질 수 있으며, 각 프레임은 FIN 비트를 통해 마지막 프레임인지 여부를 나타냅니다.
  • 텍스트와 바이너리: 텍스트 프레임(Opcode 0x1)은 UTF-8로 인코딩된 텍스트 데이터를 포함하며, 바이너리 프레임(Opcode 0x2)은 임의의 이진 데이터를 포함할 수 있습니다.
  • 제어 프레임: 제어 프레임은 연결을 관리하는 데 사용됩니다. 예를 들어, Ping(Opcode 0x9)과 Pong(Opcode 0xA) 프레임은 연결 상태를 확인하는 데 사용되며, 클로즈 프레임(Opcode 0x8)은 연결을 종료하는 데 사용됩니다.

WebSocket 한계

웹소켓 프로토콜에는 한계점이 존재하는데, 웹소켓을 지원하지 않는 브라우저가 존재하며, HTML5 이전의 기술로 구현된 서비스에는 동작하지 않습니다.

 

또한 웹소켓은 Text(Opcode 0x1), Binary(Opcode 0x2) 두 가지 타입의 메시지만 주고 받을 수 있고 메시지의 내용에 대해서는 형식이 정해져있지 않습니다.

이를 해결하기 위해 서브 프로토콜을 사용하여 주고 받는 메시지의 형식을 정해서 사용하는 경우가 많습니다. 

주로 Socket.io / STOMP를 사용합니다.