В современном веб-разработке многие приложения требуют мгновенного обмена данными между клиентом и сервером. Чаты, уведомления, совместное редактирование документов, онлайн-игры – все они нуждаются в эффективном канале связи. В этой статье мы разберём, зачем нужен WebSocket, как он работает, в чём его преимущества и недостатки, а также почему библиотека Socket.IO становится удобным инструментом для реализации реального времени в приложениях.

Содержание

  1. Введение

  2. Как работает WebSocket?

  3. Альтернативы WebSocket

  4. Socket.IO — зачем он нужен?

  5. Простая настройка Socket.IO

  6. Сравнительная таблица технологий

  7. Заключение

  8. Полезные ссылки

Введение

Зачем эта статья?

При традиционном HTTP взаимодействии клиент инициирует запрос, а сервер отвечает на него. Такой подход удобен для большинства случаев, но не подходит для задач, где требуется двусторонняя коммуникация в режиме реального времени. Именно здесь на помощь приходят WebSocket и Socket.IO.

Какие проблемы решает WebSocket?

  • Двустороннее общение: Постоянное соединение между клиентом и сервером позволяет мгновенно отправлять и получать данные.

  • Снижение задержек: В отличие от периодических запросов (polling), WebSocket обеспечивает практически мгновенную передачу сообщений.

  • Экономия ресурсов: Уменьшается нагрузка на сервер, так как не нужно постоянно устанавливать и закрывать HTTP-соединения.

Где применяется WebSocket?

  • Чаты и мессенджеры.

  • Уведомления в режиме реального времени.

  • Совместное редактирование документов.

  • Онлайн-игры и приложения с обменом данными в реальном времени.


Как работает WebSocket?

Отличие от HTTP

  • HTTP: Работает по модели запрос-ответ. Клиент посылает запрос, сервер отвечает, и соединение закрывается.

  • WebSocket: Устанавливается постоянное соединение, по которому данные могут передаваться в обоих направлениях в любое время.

Разбор протокола WebSocket

  1. Handshake: Изначально устанавливается HTTP-соединение, которое затем «апгрейдится» до WebSocket.

  2. Фреймы: После успешного рукопожатия данные передаются в виде фреймов, что позволяет эффективно управлять информацией.

  3. Двустороннее соединение: Обе стороны могут отправлять сообщения независимо друг от друга, что упрощает реализацию интерактивного обмена данными.

Преимущества и недостатки WebSocket

Преимущества:

  • Низкая задержка при передаче данных.

  • Эффективное использование канала связи.

  • Поддержка двусторонней коммуникации.

Недостатки:

  • Необходимость поддержки специального протокола на сервере.

  • Может быть сложнее интегрировать в существующую инфраструктуру.

  • Ограниченная поддержка в некоторых прокси-серверах и брандмауэрах.


Альтернативы WebSocket

Long Polling

При long polling клиент отправляет запрос к серверу, сервер держит его открытым до появления новых данных, а затем отвечает. Такой метод работает, но:

  • Это своего рода «костыль», так как создаёт дополнительную нагрузку из-за постоянных HTTP-запросов.

  • Задержки могут быть значительно выше по сравнению с постоянным соединением.

Server-Sent Events (SSE)

SSE позволяет серверу отправлять данные клиенту по единственному потоку через HTTP.

  • Подходит для уведомлений и обновлений, где требуется лишь односторонняя связь (сервер → клиент).

  • Имеет ограничения в плане двусторонней коммуникации.

Почему WebSocket не всегда лучше?

Выбор технологии зависит от конкретных задач:

  • Если требуется только одностороннее обновление (например, уведомления), может оказаться эффективнее использовать SSE.

  • В случаях, когда инфраструктура не поддерживает постоянные соединения или есть ограничения, могут использоваться альтернативные методы.


Socket.IO — зачем он нужен?

Socket.IO представляет собой надстройку над WebSocket, предоставляющую дополнительные возможности:

Основные преимущества Socket.IO:

  • Автоматический reconnect: В случае разрыва соединения клиент автоматически переподключается.

  • Fallback-режим: Если WebSocket недоступен, Socket.IO может использовать альтернативные транспортные методы (например, long polling).

  • Удобство использования: Простая настройка и богатый API позволяют быстро реализовать сложные сценарии взаимодействия.

  • Кастомизация: Возможность расширять функционал, добавлять промежуточное ПО (middleware) и обрабатывать события на стороне сервера и клиента.


Простая настройка Socket.IO

Установка и настройка сервера (Node.js / NestJS)

Для демонстрации воспользуемся простым Node.js сервером на базе Express:

// server.js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server);

// Обработка подключения клиента
io.on('connection', (socket) => {
  console.log('Пользователь подключился');

  // Обработка сообщения от клиента
  socket.on('message', (msg) => {
    console.log('Получено сообщение:', msg);
    // Отправляем сообщение обратно клиенту (эхо)
    socket.emit('message', msg);
  });

  socket.on('disconnect', () => {
    console.log('Пользователь отключился');
  });
});

server.listen(3000, () => {
  console.log('Сервер запущен на порту 3000');
});

Минимальный клиентский код (HTML + JS)

<!-- index.html -->
<!DOCTYPE html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Socket.IO Пример</title>
  </head>
  <body>
    <h1>Пример подключения Socket.IO</h1>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io();

      socket.on('connect', () => {
        console.log('Подключено к серверу');
        // Отправляем тестовое сообщение
        socket.emit('message', 'Привет, сервер!');
      });

      socket.on('message', (msg) => {
        console.log('Сообщение от сервера:', msg);
      });
    </script>
  </body>
</html>

Тестирование соединения

Запустите сервер (node server.js), затем откройте index.html в браузере. В консоли браузера и терминале сервера вы увидите сообщения о подключении и обмене данными.


Сравнительная таблица технологий

Технология

Тип соединения

Направление передачи

Производительность

Поддержка fallback

HTTP (Polling)

Request-Response

Одностороннее

Высокая задержка

Нет

Long Polling

Длительные HTTP-запросы

Одностороннее

Задержки, нагрузка на сервер

Нет

Server-Sent Events (SSE)

Постоянное (одностороннее)

Сервер → Клиент

Подходит для уведомлений

Частично

WebSocket

Постоянное соединение

Двустороннее

Низкая задержка, высокая эффективность

Нет

Socket.IO

Абстракция WebSocket с fallback

Двустороннее

Автоматический reconnect и fallback

Да


Заключение

Когда использовать WebSocket и Socket.IO?

  • WebSocket: Идеален для приложений, где требуется двусторонняя коммуникация в реальном времени и минимальные задержки. Подходит для реализации чатов, онлайн-игр и систем уведомлений.

  • Socket.IO: Выбор для проектов, где важно обеспечить стабильное соединение даже в нестабильных сетевых условиях, благодаря автоматическому переподключению и поддержке fallback.

Полезные ссылки

Автор: Каменских Валерий

Комментарии (25)


  1. bdkv
    15.02.2025 07:52

    Отличная статья, которая задаёт правильный вектор для начинающих.


    1. neon4on Автор
      15.02.2025 07:52

      Спасибо большое)


  1. flancer
    15.02.2025 07:52

    А правда, что WebSocket - это устаревшая технология и применяется только с HTTP1?


    1. neon4on Автор
      15.02.2025 07:52

      Впервые о таком слышу, что это устаревшая технология. Слушай, WebSocket сейчас как стандарт для real-time приложений поставляется. По поводу версий HTTP - WebSocket имеет нативную поддержку в 1.0 и 1.1 версиях. Т.е это просто протокол для двусторонней связи, в других версиях HTTP - у него есть замена в виде протокола WebTransport

      Спасибо за проявленный интерес)


      1. flancer
        15.02.2025 07:52

        Ну, просто я делал приложение на базе node'овских http / http2 / https серверов и в качестве прокси-сервера использовал apache. Само приложение могло использовать любой из трёх серверов, в зависимости от настроек. Так вот, веб-сокеты из браузера, через апач и в моё приложение работали только для режима HTTP1 (browser -> HTTPS -> apache -> HTTP1 -> app).

        SSE, кстати, работало и для первой, и для второй версии HTTP без проблем.


        1. Pubert
          15.02.2025 07:52

          Думаю, дело в apache...

          UPD: понял, о чём Вы. А разве нельзя использовать HTTP/1 только для одного запроса? Для установки соединения, очевидно, нет особого резона переходить на HTTP/2, так что вряд-ли вебсокеты будут работать с ним


          1. flancer
            15.02.2025 07:52

            Идея была в том, чтобы app-сервер обслуживал всё HTTP'шное на одном порту. И для HTTP/1 это работает.


    1. mayorovp
      15.02.2025 07:52

      Правда что rfc8441 слишком "свежая" (ещё 7 лет не прошло) и толком никем не поддерживается, а потому веб-сокеты по http1.1 и работают. Но это никоим образом не делает их устаревшими.


      1. flancer
        15.02.2025 07:52

        Спасибо за ссылку. Хорошо уже, что хотя бы намерения есть. Просто обычный POST фронта на бэк и SSE с бэка на фронт дают аналогичную веб-сокетам функциональность, но при этом работают через все версии протокола. Вот я и спросил :)

        Правда ещё пару-тройку лет назад nginx не проксировал HTTP/2. Надеюсь, что на сейчас ситуация исправилась, не проверял. Так что вполне себе возможно, что разрабы веб-серверов просто не успевают за разрабами протоколов.


        1. barmaglot92
          15.02.2025 07:52

          Post с фронта не аналогично ws как минимум потому что трата ресурсов на установку соединения как на клиенте так и на сервере хотя в хттп2 это слегка иначе но есть подводные камни.

          Ws это единственное апи для реалтайм задач в веб


          1. flancer
            15.02.2025 07:52

            ... которое до сих пор не проксируется по HTTP2 в самых популярных веб-серверах (apache2 & nginx). Вот и непонятка выходит, раз оно такое безальтернативно популярное, то почему так?


            1. k4ir05
              15.02.2025 07:52

              Так это ведь разные протоколы. Вебсокет использует HTTP только для установки соединения. Дальше обмен данными происходит по этому соединению. Какой смысл использовать для этого именно HTTP2?


              1. flancer
                15.02.2025 07:52

                У меня nodejs-приложение, которое стоит за прокси-сервером (apache или nginx) и обрабатывает HTTP-запросы. Часть запросов - статика, часть - API (JSON POST), часть - SSE. И всё это это прекрасно крутится на http2, который замечательно проксируется через apache, до тех пор, пока я не решу использовать веб-сокеты. Как только я решаю использовать веб-сокеты, весь мой зоопарк вынужденно съезжает на HTTP/1, потому что веб-сокеты с HTTP/2 "не дружат" (или HTTP/2 с веб-сокетами).

                Резюме по проблеме от ИИ:

                • WebSockets не работают напрямую через HTTP/2.

                • Можно использовать CONNECT для создания туннеля (прим. - на вебсокет-сервер, можно в том же nodejs-приложении, но на другом порту).

                • Для одностороннего потока можно использовать SSE.

                • Для двустороннего общения WebTransport (HTTP/2, HTTP/3) — перспективное, но пока не везде доступное решение.

                Вот и получается, что я не могу иметь одно nodejs-приложение, которое бы обрабатывало статику / API / SSE / WebSockets на одном порту по HTTP/2. По HTTP/1 всё работает прекрасно, а по HTTP/2 - мешают WebSockets.


                1. k4ir05
                  15.02.2025 07:52

                  HTTP/2 просто не поддерживает 101 статус (смену протокола).

                  И всё это это прекрасно крутится на http2, который замечательно проксируется через apache, до тех пор, пока я не решу использовать веб-сокеты.

                  А как вы их пытаетесь использовать? Вам же для этого в любом случае нужен отдельный веб-сервер, на него и следует тогда эти запросы проксировать (можно по сокетам, а наружу один порт). Либо какой-то универсальный веб-сервер, не знаю как это на nodejs делается.


                  1. flancer
                    15.02.2025 07:52

                    Как-то так:

                    /** @type {Server|Http2Server|Http2SecureServer} */
                    const server = useHttp1 ? initHttp1()
                        : (key && cert) ? initHttps(key, cert) : initHttp2();
                    
                    /** @type {WebSocketServer} */
                    const socketServer = new WebSocketServer({noServer: true});
                    const onUpgrade = createListener(socketServer);
                    server.on('upgrade', onUpgrade);

                    В зависимости от конфига выбираешь, какой из встроенных nodejs-серверов будет обрабатывать HTTP-запросы (их в node три: http, http2, https). Потом создаёшь, опять же, в зависимости от конфига, WebSocketServer и говоришь, что он работает в рамках уже существующего веб-сервера (перенаправляешь на него события onUpgrade). Таким образом, есть один веб-сервер который слушает один порт и может работать либо standalone (в деве), либо за проксирующим/балансирующим веб-сервером (на проде). Все три node-сервера по интерфейсам сопоставимы, а дальнейшему коду всё равно, как пришёл запрос (HTTP/1/2/S), вебсокеты немного в стороне, но тоже через тот же порт работают. Когда HTTP/1 - всё крутится.


                    1. k4ir05
                      15.02.2025 07:52

                      Не вижу проблем при использовании http2 запускать его на нужном порту и параллельно запускать http для ws на сокете. Разве что на винде может не сработать. Хотя, судя по доке, IPC там поддерживается, так что и на винде должно работать.


                      1. flancer
                        15.02.2025 07:52

                        Я тоже не вижу проблем, я просто спросил:

                        А правда, что WebSocket - это устаревшая технология и применяется только с HTTP1?


        1. supercat1337
          15.02.2025 07:52

          О nginx и http2 https://nginx.org/en/docs/http/ngx_http_v2_module.html.

          Что касается использования post и sse вместо ws, то полностью солидарен с Вами. Не так много категорий приложений, где именно необходим жесткий реалтайм.


  1. jhoag
    15.02.2025 07:52

    Зачем эта статья?

    И ни намёка на ответ. Почему? Потому что статья — сгенерированный нейросетью огрызок, который не раскрывает тему, а автор даже не прочитал текст перед публикацией.


    1. neon4on Автор
      15.02.2025 07:52

      Эх, кого-то похоже обделили. Не думал, что есть человеческий фактор и что я эту статью перечитывал и переписывал несколько дней? Зайди и ради интереса, хоть напиши в черновик, в Хабре, статью и больше таких мыслей не будет


      1. SSranked
        15.02.2025 07:52

        …я эту статью перечитывал и переписывал несколько дней

        В современном разработке

        До третьего слова так ни разу и не удалось дочитать, чтобу изменить «современном» на «современной»?


        1. flancer
          15.02.2025 07:52

          А вот это доказательство, что не ИИ писал - ИИ так не лажает.


  1. supercat1337
    15.02.2025 07:52

    Кстати, может быть кто-нибудь реализовывал чаты? Есть ли ощутимая разница при использовании SSE и WebSocket, если обмен данными осуществляется в текстовом формате?

    Особенно интересует нагрузка и на клиент, и на бэк при использовании этих способов. Выше справедливо отметили http/2, который может снизить сетевую нагрузку для клиента. Кто-то копал в эту сторону?


    1. flancer
      15.02.2025 07:52

      На уровне пары человек в чате - разница вряд ли будет. Я не заметил, по крайней мере. Уверен, что она есть, но для человека незаметна. Делал как-то такую поделку (чат на SSE) - вполне себе всё бодро бегало.

      Что же касается высоконагруженных приложений, то там у меня никакого опыта.


      1. supercat1337
        15.02.2025 07:52

        Спасибо за ответ! Очень интересно