HTTP, Server Sent Events (SSE), Websockets и Long Polling

Олег Кусов21.08.2022
Браузеры
JavaScript
Сеть

Существуют разные способы коммуникации клиента с сервером. В этом материале предлагаю рассмотреть HTTP, Server Sent Events, Websockets и Long Polling.

HTTP

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

HTTP был придуман Тимом Бернерс-Ли в 1989-1991 году. Первая версия 0.9 имела лишь метод GET, а запрос выглядел так:

GET /index.html

Тогда в запросе еще не было сервера и порта. А в ответ приходила HTML-разметка в чистом виде без каких-либо HTTP-заголовков. Соответственно, не было ни статус-кодов, ни возможности отправить json.

HTTP/1.0

В этой версии появились HTTP-заголовки, включая Content-Type, появились статусы как запроса так и ответа.

Запрос выглядел так:

GET /mypage.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML>
A page with an image
  <IMG SRC="/myimage.gif">
</HTML>
</pre>

Первая версия все еще не была стандартизирована.

HTTP/1.1

  • Появилась возможность не закрывать соединение при последовательных запросах на сервер.
  • Стала возможной отправка второго запроса до того как получен ответ на первый.
  • Поддержка chunked для файлов больших размеров
  • Добавлен механизм кеширования, о котором я рассказывал ранее
  • Появился host заголовок, который позволил использовать несколько доменов на одном сервере.

Благодаря своей гибкости, протокол постоянно расширялся, появлялись новые заголовки. Самым крупным изменением в 1994 году стало использование SSL - зашифрованного транспортного слоя поверх HTTP.

С годами в HTTP появились способы защиты пользователей веба. Так появились Content Security Policy и Cross Origin Resource Sharing (CORS). Первый заголовок защищает от CSRF и XSS атак, жестко ограничивая возможности по скачиванию ресурсов, будь то JS или CSS. CORS также защищает от межсайтовых атак (когда с домена злоумышленника делается запрос на другой домен, в котором пользователь авторизован).

HTTP/2.0

Современный веб сложный и ресурсоемкий. HTTP/1.1 имеет ограничение в 5-8 одновременных запросов, что делает его не очень быстрым при передаче большого числа файлов с сервера.

Протокол позволяет отправлять теоретически бесконечное количество запросов в одном соединении, таким образом, ничего не будет блокировать передачу файлов. HTTP/2.0 - бинарный протокол, со сжатием заголовков и механизмом Server Push для передачи ассетов страницы еще в момент загрузки страницы (он будет отключен в Chrome 106)

Long Polling

Это процесс, при котором соединение между сервером и клиентом не разрывается длительный период, а при разрыве повторно создается. Такой механизм позволяет передавать промежутками различные данные. Например, он неплох для отправки статусов бизнес-сущностей клиенту или любой другой real-time информации. В отличие от обычных запросов, здесь создается всего одно соединение, что уменьшает нагрузку на сервер и ускоряет передачу.

Работает Long Polling так:

  1. Клиент делает запрос
  2. Сервер принимает запрос и не отвечает до тех пор, пока ему не будет чем ответить.
  3. Клиент получает ответ и заново делает запрос.

Могут быть кейсы, когда сервер из-за слишком долгого ответа закрыл соединение, в этом случае запрос делается повторно.

Для реализации Long Polling необходимо, чтобы сервер держал соединение открытым до тех пор пока ему не будет чем ответить, поэтому реализовать Long Polling без взаимодействия с бэкендерами не получится.

Server Sent Events (SSE)

Это расширение к HTTP, с помощью которого можно с сервера отправлять запросы на клиент. Это отличная замена Long Polling. Идеально подойдет для динамически изменяемой информации в приложении. Например, различная real-time статистика, текстовые трансляции матчей и так далее. В этих случаях нет необходимости использовать WebSockets, так как клиент ничего не отправляет серверу.

Как попробовать? Необходимо на сервере в заголовках отправлять:

'Content-Type': 'text/event-stream',

На клиенте открываем соединение через EventSource и вешаем обработчик на eventSource 'message' событие:

const ev = new EventSource('');
ev.addEventListener('message', (e) => {
       console.log('data', e.data):
})

.

WebSocket

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

Браузер сначала делает HTTP-запрос с заголовками Connection: Upgrade и Upgrade: websocket для смены протокола на websocket. Сервер в ответ отправляет код 101 и соединение переключается на websocket.

Данные передаются в виде "фреймов" (фрагментов). Вот так выглядит пример отправки сообщения:

Пример чата

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);
}

На сервере необходимо хранить стейт клиентов и отправлять им сообщения при событии 'message':


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

const clients = new Set();

http.createServer((req, res) => {
  wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);
});

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

  ws.on('message', function(message) {
    for(let client of clients) {
      client.send(message);
    }
  });

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

Источник: learn.javascript

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

Обычно для работы с вебсокетами используют библиотеку socket.io.

Выводы:

Вот мы и разобрались кратко со способами коммуникации клиента с сервером. Что выбирать для вашего проекта? Зависит от кейсов. Для одних фич вам может понадобиться SSE, для других достаточно обычного HTTP 1.1.

Изображения материала были созданы с помощью нейросети. Все права защищены ©