W9D1 Websocket

Jin Bae·2023년 1월 10일
0

스파르타코딩클럽

목록 보기
27/35

전에 쓴 노트 참고
강의 자료

실시간 통신 기술 종류

1. Polling

When connecting with HTTP, it is stateless and connectionless. The client can send a request, but the server can't send a response on its own. The server must wait until the client sends a request.

The polling method resolves this issue. It periodically sends a request to check if there is any data the server wants to send or if an event has occurred.

Its con is that it severely increases the HTTP overhead (a phenomenon where process time and memory are additionally used).

Appropriate for:

  • Services where sending real-time messages aren't important
  • A server that can respond to frequent requests

2. Long Polling

Works similarly to polling but with an additional condition and settings, where:

  • One polling has a longer connection period
  • Long polling waits until an event occurs

Polling keeps sending requests where long polling increases the connection time to wait until an event occurs.

Appropriate for:

  • Services where sending real-time messages are important but doesn't have data to send frequently

3. Websocket

Sockets work in TCP and not as HTTP connections. Unlike HTTP, it is stateful and doesn't need to make periodic requests like polling.

The socket is triggered through the Websocket Handshake request to connect from HTTP to Socket.

Either the client or the server can send data.

Appropriate for:

  • Services that need communication in both directions and an environment where there is a server exclusive for socket communication.

4. Sever Sent Event

HTML5 standard technology that can send data one-way from the server to the client.

Unlike websockets, a separate protocol is not needed and unlike polling, periodic requests are not needed.

Appropriate for:

  • Services that need a one-way data from the server to the client
  • Streaming services

5. WebRTC

WebRTC communicates through P2P. It usually uses UDP and has the advantage of being fast. However, its performance can decrease as users increase as it is P2P.

Appropriate for:

  • Services where real-time communication is of utmost priority
  • Real-time services for individuals or a small group

Using the websocket

Use the following code to create a websocket. This uses the ws protocol.

let socket = new WebSocket("ws://localhost");

4 events can be used when the socket is successfully created:
1. open: Event when connected
2. message: Event when data is sent
3. error: Event when error occurs
4. close: Event when connection is disconnected

Example code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Client</title>
</head>

<body>  
    <script>
        // Websocket handshake
        let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello");

        socket.onopen = function(e) {
          alert("[open] 커넥션이 만들어졌습니다.");
          alert("데이터를 서버에 전송해봅시다.");
          // Sends data
          socket.send("My name is Bora");
        };

        socket.onmessage = function(event) {
          alert(`[message] 서버로부터 전송받은 데이터: ${event.data}`);
        };

        // Closes socket
        socket.onclose = function(event) {
          if (event.wasClean) {
            alert(`[close] 커넥션이 정상적으로 종료되었습니다(code=${event.code} reason=${event.reason})`);
          } else {
            // 예시: 프로세스가 죽거나 네트워크에 장애가 있는 경우
            // event.code가 1006이 됩니다.
            alert('[close] 커넥션이 죽었습니다.');
          }
        };

        socket.onerror = function(error) {
          alert(`[error]`);
        };
    </script>
</body>

<div id="messages"></div>
</html>

Connection status:
Use socket.readyState to check the connection status.
0: Connecting
1: Connected
2: Connection closing
3: Connection closed

Example code between client and server

Server code

const http = require('http');
const fs = require('fs');
const ws = new require('ws');

const wss = new ws.Server({ noServer: true });

const clients = new Set();

function accept(req, res) {

    // headers.upgrade (HTTP 1.1 only) is used to upgrade an established server/client connection to a different protocol (websocket)
    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade
    // 연결할 때 header에 upgrade가 포함되어있으면, websocket을 쓴다
    if (req.url == '/ws' && req.headers.upgrade && req.headers.upgrade.toLowerCase() == 'websocket' && req.headers.connection.match(/\bupgrade\b/i)) {
        wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);
    }
    else if (req.url == '/') {
        // index.html
        fs.createReadStream('./index.html').pipe(res);
    }
    else {
        // page not found
        res.writeHead(404);
        res.end();
    }
}

function onSocketConnect(ws) {
    clients.add(ws);
    console.log(`new connection`);

    ws.on('message', function (message) {

        const obj = JSON.parse(message);

        console.log("message received: ", obj);

        // 각각의 클라이언트한테 전달
        for (let client of clients) {
            // 클라이언트에게 데이터 전송
            client.send(JSON.stringify(obj));
        }
    });

    ws.on('close', function () {
        console.log(`connection closed`);
        // 클라이언트가 종료했으면 배열에서 삭제
        clients.delete(ws);
    });
}

http.createServer(accept).listen(8080);

The handleUpgrade() method is a websocket server method used when processing the request to upgrade wss HTTP. This method is called when the client is upgrading to a different protocol (in this case the websocket protocol) and has this header in the HTTP request.

For the following code

wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect)

Reference: https://github.com/websockets/ws/blob/HEAD/doc/ws.md#serverhandleupgraderequest-socket-head-callback

  • req: The client HTTP GET request
  • req.socket: The network socket between the server and client
  • Buffer.alloc(0): Creates a new buffer with a length of 0 bytes. This buffer is used as the websocket data to callback.
  • onSocketConnect: The callback function that is called when the websocket is connected

Client code

<!-- 메시지 폼 -->
<form name="publish">
    <input type="text" name="message">
    <input type="submit" value="send">
  </form>
  
  <script>
    let url ='ws://localhost:8080/ws';
    let socket = new WebSocket(url);

    // 폼에 있는 메세지 보내기
    document.forms.publish.onsubmit = function() {

      let outgoingMessage = this.message.value;      
      const obj = { "type": "message" , "params": { "value": outgoingMessage }} 
      socket.send(JSON.stringify(obj));
      return false;
    };

    // 들어오는 메세지 핸들링
    socket.onmessage = function(event) {

      let incomingMessage = event.data;
      showMessage(incomingMessage);
    };

    socket.onclose = event => console.log(`Closed ${event.code}`);

    // dev에 메세지 더하기
    function showMessage(message) {

      let messageElem = document.createElement('div');
      const obj = JSON.parse(message);
      messageElem.textContent = obj.params.value;
      document.getElementById('messages').prepend(messageElem);
    }
  </script>
  
  <!-- 수신받을 메시지가 노출될 div -->
  <div id="messages"></div>

Socket.io 이벤트 통신

// 해당 이벤트를 받고 콜백함수를 실행
socket.on('받을 이벤트 명', (msg) => {
})
 
// 이벤트 명을 지정하고 메세지를 보낸다.
socket.emit('전송할 이벤트 명', msg)

Socket.io 송수신 메소드

// 접속된 모든 클라이언트에게 메시지를 전송한다
io.emit('event_name', msg);
 
// 메시지를 전송한 클라이언트에게만 메시지를 전송한다
socket.emit('event_name', msg);
 
// 메시지를 전송한 클라이언트를 제외한 모든 클라이언트에게 메시지를 전송한다
socket.broadcast.emit('event_name', msg);
 
// 특정 클라이언트에게만 메시지를 전송한다
io.to(id).emit('event_name', data);

0개의 댓글