Как использовать Websocket на примере простого Express API?

?v=1

Краткое описание технологии


Websocket — это протокол связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени.

Для установления соединения WebSocket клиент и сервер используют протокол, похожий на HTTP. Клиент формирует особый HTTP-запрос, на который сервер отвечает определенным образом.

Примечания

Несмотря на «похожесть» новых запросов и ответов на запросы и ответы протокола HTTP, они таковыми не являются. Например, в запросе есть тело, но в заголовках поле «Content-Length» отсутствует (что нарушает соглашения HTTP). Подробнее об этом можно прочитать в Википедии.

Одним из главных преимуществ технологии — это ее простота. На клиенте и сервере есть всего 4 события для обработки:

  1. connection
  2. error
  3. message
  4. close


Почему Websocket?


Кроме ws существуют еще два способа непрерывной передачи данных: Server-Sent Events (SSE) и Long Polling.

Приведем сравнения механизмов непрерывной связи сервера и клиента, а также сделаем выводы, почему стоит (или не стоит) использовать вебсокет.


Одним из главных преимуществ технологии ws — это скорость передачи данных. SSE и LP используют протокол HTTP (S) и работают примерно так:

  1. Делаем запрос на изменения;
  2. Если изменения на сервере появились, то сервер их отправляет;
  3. Когда клиент получает изменения, клиент делает новый запрос.


Выводы:

  1. Вебсокет не требует от клиента постоянно запрашивать изменения и именно поэтому он быстрее.
  2. Вебсокет позволяет передавать бинарные данные, что не позволяет протокол HTTP (S).
  3. Вебсокет не нужно использовать, если проект требует совместимость со старыми версиями браузеров. Читать о совместимости с браузерами


Пример работы простейшего api.

const http = require("http");
const express = require( "express");
const WebSocket = require( "ws");

const app = express();

const server = http.createServer(app);

const webSocketServer = new WebSocket.Server({ server });

webSocketServer.on('connection', ws => {
   ws.on('message', m => {
webSocketServer.clients.forEach(client => client.send(m));
   });

   ws.on("error", e => ws.send(e));

   ws.send('Hi there, I am a WebSocket server');
});

server.listen(8999, () => console.log("Server started"))


Что здесь происходит?

Чтобы создать сервер поддерживающий ws, мы создаем обычный http сервер, а потом привязываем к нему при создании websocket сервер.

Функция «on» помогает управлять событиями websocket. Самым примечательным является событие message, так что рассмотрим его подробнее.

Здесь функция получает параметр m — сообщение, то есть то, что отправил пользователь. Таким образом мы можем отправить с клиента строку и обработать ее на сервере. В данном случае сервер просто пересылает это сообщение всем, кто подключен к серверу websocket. Массив clients объекта webSocketServer содержит все подключения к серверу. Объект ws в то же время хранит данные только об одном подключении.

Замечание

Не стоит использовать такой подход в реальном приложении. Если описать api таким образом, то сервер не может отличить один запрос от другого. О том, как можно построить api на основе websocket будет написано далее.

Взаимодействие с сервером на клиенте будет выглядеть так:

export const wsConnection = new WebSocket("ws://localhost:8999");
wsConnection.onopen = function() {
    alert("Соединение установлено.");
};

wsConnection.onclose = function(event) {
    if (event.wasClean) {
        alert('Соединение закрыто чисто');
    } else {
        alert('Обрыв соединения'); // например, "убит" процесс сервера
    }
    alert('Код: ' + event.code + ' причина: ' + event.reason);
};

wsConnection.onerror = function(error) {
    alert("Ошибка " + error.message);
};

export const wsSend = function(data) {
// readyState - true, если есть подключение
    if(!wsConnection.readyState){
        setTimeout(function (){
            wsSend(data);
        },100);
    } else {
        wsConnection.send(data);
    }
};


API на основе Websocket


В отличие от REST API, где запросы распределены по разным url, Websocket API имеет только один url. Для того, чтобы построить полноценное API на основе вебсокетов, необходимо научить систему отличать один запрос от другого. Это можно реализовать следующим образом:

1) С клиента мы будем передавать запросы в виде строки-json, которую распарсим на сервере:

const sendMessage = (message) => conn.send(JSON.stringify({ event: "chat-message", payload: { userName, message }}));


2) На сервере мы распарсим строку и выделем в ней поле event — тип запроса. Пропишем для каждого типа соответствующий ответ:

const dispatchEvent = (message, ws) => {
   const json = JSON.parse(message);
   switch (json.event) {
       case "chat-message": webSocketServer.clients.forEach(client => client.send(message));
       default: ws.send((new Error("Wrong query")).message);
   }
}


Таким образом мы можем отправлять разные запросы на сервер и обрабатывать ответ в зависимости от запроса.

Заключение


Если вам была дана задача сделать API и вы узнали, что поддержка старых браузеров заказчика не интересует, то API на основе WebSocket — отличный выбор. Для вашего удобства мы подготовили код клиентской и серверной части по ссылке.

© Habrahabr.ru