itmo_conspects

Лекция 8. Подписочная модель обмена сообщениями

Протокол HTTP устроен так, что взаимодействие между сервером и клиентом инициируется клиентом. Зачастую сервер ничего не знает о том, как подключиться к клиенту

Поэтому есть несколько способов передавать сообщения с сервера клиенту

Частые опросы

Частые опросы (или поллинг, Periodic polling) - самый простой способ

Работает так: надо сказать клиенту, чтобы он отправлял запрос на получение новых сообщений с сервера, например, каждые 10 секунд. В ответ сервер понимает, что клиент находится в сети, и отправляет все уведомления, которые у него накопились на этот момент

У такого подхода есть недостатки:

Длинный опрос

Длинный опрос (Long polling) - простой способ поддерживать соединение с сервером, не используя другие протоколы. Работает это так:

  1. Клиент отправляет запрос на сервер
  2. Сервер, не закрывая сетевое соединение, ждет, пока появится сообщение клиенту
  3. Когда появляется, сервер отсылает его и закрывает соединение
  4. Клиент отправляет новый запрос

Если соединение в каком-либо случае потеряно (например, при переключении Wi-Fi), то браузер пытается заново открыть соединение с сервером

Клиентский код для длинного опроса может выглядеть так:

async function subscribe() {
    const response = await fetch("/subscribe");

    if (response.status == 502) {
        // Таймаут соединения
        await subscribe();
    } else if (response.status != 200) {
        // Неправильный код ошибки
        console.log(response.statusText);

        await new Promise(resolve => setTimeout(resolve, 1000));
        await subscribe();
    } else {
        // Успешная передача
        const data = await response.json();
        console.log(data);
        await subscribe();
    }
}

Такой подход хорошо работает, когда сообщения приходят редко

Однако реализация сетевой библиотеки должна поддерживать работу со многими ожидающими подключениями. Например, Node.js не создает поток для каждого нового соединения, что позволяет выделять меньше памяти и не создавать блокирующие потоки для ожидания данных. Это возможно благодаря кроссплатформенной библиотеке LibUV, написанной на C, для работы с сеть

WebSocket

WebSocket (или WS сокращенно) - протокол для совершения постоянного взаимодействия между сервером и клиентом

Протокол WebSocket особенно полезен для сервисов, которые ведут постоянное общение с клиентом, например, игры, торговые площадки и другие

Чтобы создать подключение, нужно

let socket = new WebSocket("wss://example.com")

Здесь протокол wss - это безопасное соединение (от WebSocket Secure) с использованием TLS. Аналогично ws - это протокол без TLS

Далее такой сокет генерирует 4 события:

Через сокет сообщения можно посылать через метод send

Само соединение устанавливается с помощью протокола HTTP. Клиент отправляет HTTP-запрос с заголовками Connection: Upgrade и Upgrade: websocket, тем самым спрашивая, может ли сервер перейти на общение по протоколу WebSocket. Если сервер поддерживает WebSocket, то он отправляет ответ со статусом 101 Switching Protocols. Такая процедура называется обновлением протокола

Поток данных в протоколе WebSocket состоит из фреймов - фрагментов данных, которые могут быть отправлены любой стороной. Фреймы могут быть:

Сам по себе WebSocket не описывает функционал переподключения, аутентификацию и другое, но зато это реализуют библиотеки, например, Socket.IO

Socket.IO - библиотека JavaScript, использующая веб-сокеты или другие технологии (например, Flash Socket), если веб-сокеты не доступны

Также, в отличие от веб-сокетов Socket.IO: