[WebSocket] 웹소켓을 알아봅시다.
HTTP (HTTP/1.1 이하)
클라이언트가 서버에게 요청(Request)을 보내고 서버가 그에 응하여 응답(Response)을 보내는 식으로 단방향 소통
서버가 클라이언트에게 메시지를 보내는 건 클라이언트의 요청에 응답을 할 때 뿐
서버가 먼저 클라이언트에게 메시지를 보내지 않는다.
채팅앱
내가 상대방에게 메시지를 보내는 것: 클라이언트 → 서버
상대방이 나에게 메시지를 보냈을 때: 서버 → 클라이언트
⇒ 서버가 먼저 클라이언트에게 메시지를 보내지 않는다.
⇒ 내 앱에서 감지하는 것이 불가능
- 서버에 들어온 상대방 메시지를 서버가 보낼 수 없음
- 클라이언트의 요청 없이 서버에서 메시지를 보내지 못하기 때문
HTTP를 사용해서 문제 해결방법
- Polling
- 클라이언트가 주기적으로 서버에 요청을 보내서 상대가 새 챗을 보냈는 지 확인
- 서버는 업데이트가 있을 때 있다고 응답, 없다면 없다고 응답
문제
- 요청을 보내는 주기만큼의 지연 발생
- 서버가 갖고 있는 상태의 변화에 즉각적으로 반응하지 못함
- 계속해서 불필요한 요청 전송
- 요청의 주기가 짧다면 반응 속도는 빨라짐, 트래픽 낭비 심해짐
- Long Polling
- 서버가 클라이언트의 요청에 바로 응답하지 않고 업데이트가 발생할 때까지 대기
- 상대방이 챗을 보내거나 타임아웃으로 설정된 시간이 지나면 응답
- 응답이 오면 클라이언트는 바로 다시 요청을 보냄
문제
- 데이터의 업데이트에 반응하는 속도 빨라짐, 서버의 부담이 커짐
- 서버가 클라이언트로부터 요청을 받을 때부터 응답을 보내기까지 클라이언트와의 연결이 지속됨
- 동시에 여러 클라이언트가 서비스를 사용하면 그만큼의 연결을 유지해야 하므로 부하 발생
- ⇒ 업데이트에 대한 반응이 느려짐
- HTTP 요청과 응답에 포함되는 헤더 정보의 양도 매 번 부담
⇒ 클라이언트와 서버가 동등하게 메시지를 주고받을 수 있는, 양방향 통신이 가능한 방식이 필요
HTTP/2와 WebSocket 비교
HTTP/2는 공중전화 부스에서의 제한된 통화
- 장시간 양방향 통신을 위해 설계한 것이 아님
- 서버에서 푸시를 보내는 게 가능하지만 클라이언트의 초기 요청에 대한 응답의 일부이다.
- 연결의 지속을 위해 주기적인 요청과 응답이 필요
- 브라우저에서의 사용에는 아직 제한되는 부분이 많다.
WebSocket은 스마트폰을 통한 자유로운 통화
WebSocket 통신
- 클라이언트에서는 서버에게 WebSocket을 연결하자는 요청을 HTTP를 통해 보냄.
- 서버는 수락하는 응답을 HTTP 로 보냄
(handshake)
-
연결이 이뤄지고 나면 그 때부터 클라이언트와 서버는 HTTP가 아닌 WebSocket 프로토콜을 사용하여 소통
(ws:// or wss://)
- 클라이언트와 서버는 자유롭게 서로에게 메시지를 보낼 수 있다.
- 한 쪽이 close 프레임을 보내면 다른 쪽이 이를 확인하고 close 프레임을 응답으로 보냄으로써 연결이 종료
- 비정상적인 종료를 감지 방법
- 지정된 시간동안 메시지가 없을 시 확인 패킷을 보내는 방법
- 주기적으로 ping, pong 프레임을 주고받아서 서로의 접속 여부를 확인하는 방법
- 비정상적인 종료를 감지 방법
카카오톡 채팅의 경우 방을 나가지 않는 이상 계속해서 클라이언트와 서버가 연결되어있는건가? ⇒ 비효율적이지 않나? - 어쩔 수 없나 댓가
장점
- 헤더의 크기가 작고 오버헤드가 적기 때문에 HTTP보다 효율적인 통신이 가능
- 하나의 연결을 끝까지 유지하고, 그 과정에서도 적은 자원만 소모하기 때문에 Long Polling만큼 서버에 부담을 주지 않음
Handshake
-
클라이언트의 요청의 헤더 (우편물에 붙은 송장)
Upgrade: websocket
,Connection: Upgrade
: HTTP 연결을 WebSocket 프로토콜로 업그레이드 하자Sec-WebSocket-Key
: 클라이언트가 랜덤으로 생성한 값을 Base64로 인코딩한 문자열
-
서버의 응답의 헤더
Sec-WebSocket-Accept
: 서버는 요청을 받고 나서, GUID라 불리는 정해진 문자열을 그 키에 이어붙인 뒤 SHA-1 해시로 계산하여 다시 base64로 인코딩
-
클라이언트는 이것이 자기가 보낸 키로부터 생성된 값이 맞는지 확인
- 클라이언트는 이 응답이 자신 요청을 보낸 상대로부터 온 것임을 확인
- 자유롭게 양방향 소통 시작 (Open Connection)
TCP 소켓 / UDP 소켓
WebSocket - 7 layer (Application layer)
TCP socket, UDP socket - 4 layer (Transport layer)
WebSocket은 TCP 소켓을 기반으로 작동
- 데이터의 순서와 신뢰성 보장
WebSocket 한계
- 서버의 설계에 따라 구현이 복잡
- 특히 로드 밸런싱이 적용된 서버에서는 고려하고 설정할 부분이 많아짐
- 웹 소켓은 특정 서버와의 지속적인 연결 안에서만 이뤄지기 때문에 한 서버와 웹 소켓 통신을 시작하면 이후로도 계속 그 서버로만 데이터가 전송되도록 설정해야함
- ⇒ NGINX, HAProxy, AWS ELB 등 WebSocket을 처리할 수 있는 로드 밸런서를 선택하여 구성한다
- 메시지의 크기가 제한
- 브라우저, 서버, 네트워크 환경마다 WebSocket에서의 메시지 크기에 제약을 둘 수 있음
- 대용량 데이터의 경우, 분할해서 전송하거나 다른 프로토콜을 사용하는 등의 방법 사용
- WebSocket의 기본 프로토콜인 WS은 통신이 암호화되어 있지 않다
- 보안이 중요한 서비스라면 SSL/TLS 인증서를 발급받은 뒤 이를 사용하여 WSS를 설정 (https 방식과 비슷)
- Polling 등의 방식보다는 덜 하지만 WebSocket도 서버에 부담을 준다.
- 많은 사용자들이 동시에 접속해 있을수록 유지해야 하는 TCP 연결이 많아지고 메시지들이 오가는 빈도가 높다면 네트워크 대역폭과 CPU 사용량 증가
* 로드 밸런싱 : 서버 여러 대가 클라이언트 요청을 나눠서 받는 방식