[TIL] 웹소켓

Kyoorim LEE·2023년 9월 6일
0

스스로TIL

목록 보기
28/34

웹소켓이 뭐야?

두 프로그램 간 메세지를 교환하기 위한 통신방법 중 하나

클라이언트가 서버에 http 요청을 보내면 해당 서버는 클라이언트에게 데이터를 전달해준다.

통상적으로 http 통신은 클라이언트가 요청을 보내야만 서버가 응답을 해주는 방식이다. 서버가 먼저 요청을 보내는 경우는 절대 없다. 이런 통신 방식을 단방향 통신이라고 한다.

근데 만약에 실시간으로 업데이트 된 정보를 보여줘야한다고 했을 때,

실시간으로 http요청을 계속 날리는 방법도 있겠지만(polling, 폴링) 그거보다 더 효율적인 방법은 서버가 알아서 보내주는 것!

그 해결책 중 하나로 웹 소켓이 있다.

TCP통신 기반으로 클라이언트와 서버가 양방향 통신이 가능해진다.

양방향 통신이란,

  • 데이터 송수신을 동시에 처리할 수 있는 통신방법을 말한다
  • 클라이언트와 서버가 서로 원할 때 데이터를 주고 받을 수 있다
  • 이를 통해 실시간 네트워킹이 가능해진다 (채팅, 주식, 실시간 검색어, 옥션..)

웹소켓 동작 방법 - 핸드 쉐이킹

  • 웹소켓은 HTTP를 이용해 연결을 수립한다. 연결 수립은 핸드쉐이크(서버<-> 클라이언트)를 통해 이뤄진다.
  • 한번의 HTTP 요청과 한번의 HTTP 응답으로 이뤄진다.
  1. 클라이언트가 HTTP로 웹소켓 연결 요청을 한다.
    ```
    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade // or Upgrade: websocket => 웹소켓 요청임을 표시
    Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== // 핸드쉐이크 응답을 검증할 키값
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13
    Origin: http://example.com
    ```
  2. 서버가 HTTP로 응답한다.
    HTTP/1.1 101 Switching Protocols // 정상적인 응답의 상태코드: 101
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
    Sec-WebSocket-Protocol: chat
  • 핸드쉐이크가 끝나면 HTTP 프로토콜을 웹소켓 프로토콜로 변환하여 통신한다.

핸드쉐이킹이 완료되면 프로토콜이 ws로 변경됨

메세지 단위로 데이터를 주고받음

웹소켓 통신은 '프레임' 단위로 이뤄진다. 프레임은 데이터 조각을 말한다. 프레임은 서버와 클라이언트 양측에서 보낼 수 있다. 프레임 내 담긴 데이터 종류에 따라 프레임을 다음과 같이 분류할 수 있다.

  • 텍스트 프레임 : 텍스트 데이터가 담긴 프레임
  • 이진 데이터 프레임(binary data frame): 이진 데이터가 담긴 프레임
  • 핑퐁 프레임: 커넥션이 유지되고 있는지 확인용으로 사용하는 프레임. 서버나 브라우저에서 자동 생성헤서 보냄
  • 종료 프레임(connection close frame)

브라우저 환경에서 개발자는 텍스트나 이진 데이터 프레임만 다루게 됨

WHY?

WebSocket의 .send() 메서드는 텍스트나 이진 데이터만 보낼 수 있기 때문

프레임 헤더 구조

END
Opcode
Length

데이터 양방향 전송 완료

연결 주체(브라우저나 서버) 중 한 쪽에서 커넥션 닫기(close)를 원하는 경우 보통 숫자로된 코드와 문자로 된 사유가 담긴 connection close frame을 전송함

// close를 요청한 주체
socket.close(1000, "Work complete"); //  socket.close([code], [reason])

// close 요청을 받은 주체
socket.onclose = event => {
	//event.code === 1000
  	//event.reason === "작업 완료"
}
  • 자주 사용하는 코드
    - 1000 : 기본값. 정상 종료를 의미함
    • 1006: 1000 같은 코드를 수동으로 설정할 수 없을 때 사용. 커넥션이 유실(no close frame)되었음을 의미함

STOMP


채팅앱 만들기

A. 클라이언트 쪽 처리

1. 커넥션 생성하기

2. form 제출하기 - socket.send(message)를 사용해 message 전송

3. 메시지 수신 처리하기

let socket = new WebSocket("wss://javascript.info/article/websocket/chat/ws"); // 클라이언트가 서버에 웹소켓으로 통신하고 싶다고 요청을 보냄

// 폼에 있는 메시지를 전송
document.forms.publish.onsubmit = function() {
  let outgoingMessage = this.message.value;

  socket.send(outgoingMessage); // 웹소켓 연결 후 클라이언트가 서버에 웹소켓으로 메세지 보내기
  return false;
};

// 메시지를 수신하고, 수신한 메시지를 div#messages에 보여줌
socket.onmessage = function(event) {
  let message = event.data;

  let messageElem = document.createElement('div');
  messageElem.textContent = message;
  document.getElementById('messages').prepend(messageElem);
}
  • ws vs wss
    - ws://blah
    - wss://blah => 데이터 보안을 위해 SSL을 적용한 프로토콜

B. 서버 쪽 처리

1. 클라이언트 요청을 수락하고 http 통신을 웹소켓으로 업그레이드 해주기

2. 웹소켓 메세지를 받을 때마다 clients.add(socket)에 추가해주고 message의 이벤트리스너에 set 해준다

3. 메세지를 받으면 모두에게 보내준다

4. 연결 종료 : clients.delete(socket)

// 클라이언트의 웹소켓통신 요청을 허락해 줌. http통신을 웹소켓으로 업그레이드 해줌
const ws = new require('ws');
const wss = new ws.Server({noServer: true});

const clients = new Set();

http.createServer((req, res) => {
  // here we only handle websocket connections
  // in real project we'd have some other code here to handle non-websocket requests
  wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);
});

function onSocketConnect(ws) {
  clients.add(ws);

  ws.on('message', function(message) { //클라이언트가 보낸 웹소켓 메세지를 서버가 수신
    message = message.slice(0, 50); // max message length will be 50

    for(let client of clients) {
      client.send(message); // send message back => 클라이언트에게 다시 메세지 보내기
    }
  });

  ws.on('close', function() {
    clients.delete(ws);
  });
}

ws 대신 socket.io 라이브러리를 쓰면 좋은 이유

  • 연결 끊기면 자동 재접속 기능
  • 웹소켓 접속자마다 자동으로 id가 부여됨
  • 모든 웹소켓 유저에게 전체 메세지 전송할 수 있음
  • 웹소켓방 생성 가능

참고자료

profile
oneThing

0개의 댓글