HTTP(HyperText Transfer Protocol)
- HTTP는 단방향 통신
- 서버 프로세스는 클라이언트보다 먼저 실행되어 대기하고, 클라이언트의 요청에 항상 응답할 준비를 하고 있다.
- 클라이언트의 요청이 선행되어야만 서버의 응답이 뒤따라올 수 있는 방식
📌 HTTP로 채팅을 구현하려면?
1. Polling 방식
- 클라이언트가 서버에 일정한 주기로 요청을 보내고 응답을 받는 방식
- 가장 기본적인 기법. 클라이언트와 서버 모두 구현이 간단하다.
- 쓸모없는 요청과 응답때문에 많은 트래픽이 낭비된다. (HTTP 연결을 맺고 끊는 자체가 비용이 큰 작업이다)
- 다음 폴링이 이루어지기 전까지 어떤 이벤트가 발생했는지 모른다. (실시간성이 보장되지 않는다)
- 요청 주기를 조절할 수 있지만, 요청 주기가 짧으면 서버에 부하를 줄 수 있다.
- 실시간 메시지 전달이 크게 중요하지 않은 서비스에 적합
2. Long Polling 방식
- 클라이언트가 서버로 HTTP 요청을 일단 날린 후, 계속 기다린다.
- 서버에서 클라이언트로 전달할 이벤트가 발생하면 그 순간 서버가 응답 메시지를 전달하면서 연결이 종료된다.
- 클라이언트는 곧바로 HTTP 요청을 다시 날려서 서버의 다음 이벤트를 다시 기다린다.
(응답을 기다리다가 응답이 오면 데이터를 처리함과 동시에 새로운 접속을 생성한다)
⇒ 클라이언트는 정보를 받는 즉시 연결을 끊고 다시 요청을 보냄으로써, 마치 영구적인 연결처럼 보이게 된다.
- 즉, 반복적인 요청을 없애고 유효한 이벤트가 발생하면 응답
- Long 이라는 이름이 붙은 이유는 오랜 접속을 유지하기 때문
- 무한정 기다리는 것이 아니라, 일정 시간이 지나면 접속을 완료하고 새로 요청
- Polling 방식에 비해 불필요한 요청과 응답을 줄일 수 있다.
(근데, 사실 서버 → 클라이언트로 좁은 시간 간격으로 이벤트가 전달된다면 큰 차이는 없다)
- 누군가 말 한마디 하는 순간 100명의 유저가 동시에 응답을 받고, 다시 응답을 받기 위한 요청을 100명 모두가 동시에 보낸다. (다수의 클라이언트에게 동시에 이벤트가 발생될 경우, 곧바로 다수의 클라이언트가 서버로 접속을 시도한다)
⇒ 순간적으로 요청이 쌓이게 되어 서버가 버벅대거나, 심하면 뻗을 수도 있다.
⇒ 참여하는 유저의 수가 많은 경우에 불리하다.
- Polling 방식은 유저가 아무것도 안해도 쓸데없이 리소스를 먹는다는 단점이 있다면, Long-Polling 방식은 유저들이 아무말도 안할 때는 리소스를 거의 먹지 않고 있다가 누군가가 말하기 시작하면 갑자기 리소스를 미친듯이 잡아먹는다.
3. Streaming 방식
- 클라이언트가 서버로 보낸 요청에 대한 응답을 완료하지 않은 상태로 계속 데이터를 내려받는다.
- 응답이 올 때마다 새로 요청을 보내야하는 Long Polling에 비해 효율적이며, 서버의 상태 변경이 잦은 경우에 더 효율적이다.
- 연결을 길게 맺고 있는 경우에 유효성 관리 등의 부담이 생긴다.
(스트리밍 방식도 보통 특정 주기로 연결을 재설정하도록 구현한다)
- TCP 포트를 이용하기 때문에 스트리밍을 하면서 동시에 TCP 포트로 읽고 쓰기를 하는 것이 불가능하다.
⇒ 처음 요청을 보낸 후 연결을 끊지 않고 계속 유지하기 때문에, 서버는 클라이언트로 계속해서 메시지를 보낼 수 있지만 반대로 클라이언트는 서버로 요청을 보내는 것이 불가능하다.
⇒ 스트리밍 중 요청을 보내고 싶다면 TCP 포트 외의 다른 포트를 사용해야 한다.
🔌 WebSocket 방식
- HTML5의 표준 기술
- HTTP를 기반으로 하면서, HTTP의 문제점을 해결하는 것을 목표로 등장
■ Connection-oriented: 소켓 연결을 유지하는 동안 송수신을 동시에 처리할 수 있다.
HTTP를 업그레이드하여 이용하기 때문에 최초 접속 시 HTTP handshake를 이용한다.
(동일하게 80 포트 혹은 443 포트를 사용한다)
⇒ 추가적인 방화벽 설정이 필요없지만, 80 포트나 443 포트 이외의 다른 포트를 사용하면 방화벽/프록시에 의해 문제를 일으킬 수 있다.
■ Stateful: 접속을 하면 계속해서 연결 상태를 유지하기 때문에 네트워크 연결 비용을 아낄 수 있다.
- 양방향 통신
- 실시간성 통신이 필요한 경우에 사용
- 클라이언트의 요청 없이도 서버가 클라이언트로 데이터를 전송할 수 있다.
- HTTP와 동일한 포트를 사용하므로 HTTP 규격인 CORS나 인증 등의 과정을 기존과 동일하게 가져갈 수 있다.
- 서버와 클라이언트의 소켓 연결 자체가 일단 비용이 큰 작업이기 때문에 트래픽이 큰 경우 CPU 부담이 크다.
- Stateful 연결이기 때문에 서버와 클라이언트 연결을 계속 유지하기 위해 자동 재연결을 고려해야 하고, 비정상적으로 연결이 끊어졌을 때를 대비해야한다.
* Websocket Handshake
맨 처음에 클라이언트가 handshake 요청을 보낸다.
이 때 요청에는 Upgrade: websocket 헤더와 랜덤하게 생성된 키가 담긴 채로 전송된다.
("HTTP → Websocket 프로토콜로 업그레이드 해라")
그러면 서버는 해당 요청에 따라 프로토콜을 바꾼다는 상태 코드인 101 Switching Protocols로 응답을 한다.
(전송된 키를 바탕으로 생성한 토큰도 함께 동봉)
결과적으로 웹소켓을 위한 새로운 소켓이 만들어지고 해당 소켓을 이용해 통신을 하게 된다.
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
* Protocol Overhead
웹소켓은 Protocol Overhead 방식을 사용한다.
Protocol Overhead 방식은 여러 TCP 커넥션을 생성하지 않고 하나의 80번 포트 TCP 커넥션을 이용하고, 별도의 헤더 등으로 논리적인 데이터 흐름 단위를 이용하여 여러 개의 커넥션을 맺는 효과를 내는 방식이다.
⛔ 구형 브라우저 이슈
웹소켓은 HTML5의 기술이기 때문에 오래된 버전의 웹 브라우저는 웹소켓을 지원하지 않는다.
(익스플로러 구 버전 사용자들은 웹소켓으로 작성된 웹페이지를 볼 수 없다.)
이에 대한 해결책으로는 크게 Socket.io와 SockJS를 고려해볼 수 있었다.
Socket.io와 SockJS는 브라우저 호환성을 위해 자체 스펙으로 WebSocket을 한 번 더 감싼 솔루션이라고 보면 된다.
Socket.io
- 웹페이지가 열리는 브라우저가 웹소켓을 지원하면 웹소켓 방식으로 동작하고, 지원하지 않는 브라우저라면 일반 HTTP를 이용해서 실시간 통신을 흉내낸다.
■ 브라우저에 FlashSocket이라는 기술을 지원하는 플러그인이 설치되어있다 ⇒ 그걸 사용
■ 만약 해당 플러그인이 없다 ⇒ AJAX Long Polling 방식을 사용
- Node.js 기반. 100% 자바스크립트로 구현했다.
- 거의 모든 웹 브라우저와 모바일 장치를 지원하는 실시간 웹 애플리케이션 지원 라이브러리
(자바스크립트를 이용하여 브라우저 종류에 상관없이 실시간 웹을 구현할 수 있게 만든 기술)
- Node.js, 즉 자바스크립트로 구현된 솔루션이므로 서버, 클라이언트 모두 자바스크립트로 개발하는 것이 안정적이다. 따라서 자바 개발자들에게는 적합하지 않다.
SockJS
- Spring framework에서 브라우저 호환성 문제를 해결하기 위한 대안으로 SockJS를 권장한다.
즉, 자바 스프링에서 안정적으로 사용할 수 있다는 뜻.
- 서버 개발 시 스프링 설정에서 일반 webSocket으로 통신할 지 SockJS 호환으로 통신할 지 결정할 수 있다.
- 클라이언트는 SockJS client를 통해 서버와 통신한다.
- 기본적으로 WebSocket을 이용하기 때문에 interface가 동일하다. 차이점은
■ schema로 ws가 아닌 http를 사용한다.
■ WebSocket과 같이 브라우저에서 제공되는 라이브러리가 아닌, 외부 라이브러리를 사용하는 것이다.
■ IE 6 이상부터 지원한다.
■ 서버 측에서도 WebSocket이 아닌 SockJS server 라이브러리를 사용한다.
- 일반 WebSocket API를 사용할 때와 SockJS를 사용할 때 클라이언트 측의 코드를 비교해보자.
// WebSocket
var ws = new WebSocket('ws://domain/endpoint');
ws.onopen = function() {
console.log('open socket');
}
ws.onmessage = function(message) {
console.log('message', message.data);
}
ws.onclose = function(e) {
console.log('close');
}
// SockJS
var sock = new SockJS('http://domain/endpoint');
sock.onopen = function() {
console.log('open socket');
}
sock.onmessage = function(message) {
console.log('message', message.data);
}
sock.onclose = function(e) {
console.log('close');
}
📧 STOMP(Simple Text Oriented Message Protocol)
- WebSocket은 그냥 통신 프로토콜일 뿐, 특정 주제를 구독한 사용자에게만 메시지를 보내는 방법 혹은 특정 사용자에게 메시지를 보내는 방법과 같은 내용들은 정의하지 않는다.
- 그래서 채팅을 구현하려면 Text 지향의 Message Protocol인 STOMP가 필요하다.
- 텍스트 데이터뿐만 아니라 바이너리 데이터도 지원한다. (stomp.js version 5를 사용해야 함)
- TCP 또는 WebSocket 같은 양방향 네트워크 프로토콜을 기반으로 동작한다.
- Spring에 종속적이다.
- 기본적으로 subscribe를 통해서 구독하면, send를 통해 데이터가 송신될 때마다 해당 데이터를 전달받을 수 있다.
- 클라이언트 측에서는 클라이언트용 STOMP인 stomp.js를 사용한다.
참고
https://d2.naver.com/helloworld/1336
'네트워크' 카테고리의 다른 글
WebSocket 채팅 클라이언트 구현기 (0) | 2021.08.20 |
---|---|
웹 소켓 연결 끊김 감지하기 (0) | 2021.07.25 |
private IP 만으론 인터넷 연결이 불가능한가? (0) | 2021.07.24 |
소켓 기본 개념 정리 (0) | 2021.07.24 |
WebSocket 시작하기 (0) | 2021.07.20 |
댓글