Что такое WebSockets и как реализовать live-обновления на фронте и бэке

10 минут чтения
Средний рейтинг статьи — 4.9

Современные веб-приложения требуют не только отображения статических данных, но и мгновенной реакции на изменения — будь то новые сообщения в чате, обновления курсов валют, уведомления или онлайн-игры. Классический HTTP-запрос «клиент → сервер» плохо подходит для подобных задач, ведь данные передаются только по инициативе клиента.

Здесь на помощь приходят WebSockets — технология, позволяющая устанавливать постоянное двунаправленное соединение между клиентом и сервером.

Как работают WebSockets

  • При первом подключении клиент отправляет HTTP-запрос с заголовком Upgrade, предлагая серверу перейти на протокол WebSocket.
  • Если сервер поддерживает WebSockets, соединение «апгрейдится» и открывается двунаправленный канал.
  • Теперь клиент и сервер могут обмениваться сообщениями в реальном времени, без дополнительных запросов.

В отличие от polling (регулярного опроса сервера) или long-polling (длинных висящих запросов), WebSocket-соединение:

  • экономит трафик (нет постоянных HTTP-заголовков),
  • снижает задержку доставки,
  • может работать в обе стороны: и сервер, и клиент могут первыми инициировать отправку.
// Установка соединения
const socket = new WebSocket('ws://localhost:3000');
 
// Событие открытия соединения
socket.addEventListener('open', () => {
  console.log('Соединение установлено');
  socket.send('Привет, сервер!');
});
 
// Получение сообщений от сервера
socket.addEventListener('message', (event) => {
  console.log('Сообщение от сервера:', event.data);
});
 
// Обработка закрытия
socket.addEventListener('close', () => {
  console.log('Соединение закрыто');
});

Использование на бэкенде (пример на Node.js с ws)

Установим библиотеку:

npm install ws

Простейший сервер:

import { WebSocketServer } from 'ws';
 
const wss = new WebSocketServer({ port: 3000 });
 
wss.on('connection', (ws) => {
  console.log('Новый клиент подключён');
 
  ws.on('message', (message) => {
    console.log('Получено:', message.toString());
    ws.send(`Эхо: ${message}`);
  });
 
  ws.on('close', () => {
    console.log('Клиент отключился');
  });
});

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

Безопасность при работе с WebSockets

Так как WebSocket-соединение остаётся открытым длительное время, важно заранее подумать о безопасности. Вот три основных практики:

Аутентификация соединений

Используйте JWT-токены или другие методы проверки пользователя.
Пример: клиент подключается к ws://localhost:8080?token=JWT_HERE, а сервер валидирует токен при апгрейде соединения.

import http from 'http';
import WebSocket, { WebSocketServer } from 'ws';
import jwt from 'jsonwebtoken';
 
const server = http.createServer();
const wss = new WebSocketServer({ noServer: true });
 
// апгрейд соединения с аутентификацией
server.on('upgrade', (req, socket, head) => {
  try {
    const url = new URL(req.url, `http://${req.headers.host}`);
    const token = url.searchParams.get('token');
 
    if (!token) throw new Error('No token provided');
 
    // верификация JWT-токена
    jwt.verify(token, 'your-secret-key', (err, decoded) => {
      if (err) {
        socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
        socket.destroy();
        return;
      }
      // посылаем WebSocket-хэндшейк
      wss.handleUpgrade(req, socket, head, (ws) => {
        ws.user = decoded;
        wss.emit('connection', ws, req);
      });
    });
  } catch {
    socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
    socket.destroy();
  }
});
 
wss.on('connection', (ws) => {
  ws.send('Добро пожаловать! Аутентификация прошла успешно ✅');
});
 
server.listen(8080);

В примере создаётся обычный HTTP-сервер:

const server = http.createServer();
const wss = new WebSocketServer({ noServer: true });

Почему так?

  • HTTP-сервер слушает порт и принимает все входящие соединения.
  • Параметр noServer: true говорит, что WebSocket-сервер сам порт не открывает.
  • Вместо этого мы вручную перехватываем запросы на апгрейд (server.on('upgrade')), проверяем токен и только потом передаём соединение в wss.handleUpgrade(...).

Такой подход позволяет объединить обычные HTTP-запросы и WebSocket-подключения на одном порту и встроить аутентификацию прямо в процесс апгрейда.

Rate limiting (ограничение частоты сообщений)

Чтобы избежать спама и DoS-атак, ограничивайте количество сообщений от клиента.

import rateLimit from 'ws-rate-limit';
 
const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 минута
  max: 100, // не более 100 сообщений
  message: 'Слишком много сообщений, замедлитесь!',
});
 
wss.on('connection', (ws) => {
  limiter(ws);
  ws.on('message', (msg) => {
    console.log('Получено:', msg.toString());
  });
});

Где применять WebSockets

  • Чаты и мессенджеры
  • Онлайн-игры
  • Финансовые системы (котировки, биржевые данные)
  • Совместное редактирование документов
  • Live-уведомления (например, новые комментарии или лайки)

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

В ряде случаев вместо WebSockets можно использовать Server-Sent Events (SSE) или WebRTC (для P2P-связи). Но WebSockets — наиболее универсальное решение для большинства задач real-time.

Итог

WebSocket — это современный способ устанавливать постоянное соединение между браузером и сервером.

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

Методы:

  • socket.send(data)
  • socket.close([code], [reason])

События:

  • open
  • message
  • error
  • close

Однако сам по себе WebSocket не включает механизмы повторного подключения, аутентификации и другие высокоуровневые возможности. Для этого существуют клиентские/серверные библиотеки, но также можно реализовать всё вручную.

Опубликовано 2 сентября 202510 минут чтенияГригорий Чалый
Средний рейтинг статьи — 4.9

Настроить мониторинг за 30 секунд

Надёжные уведомления о даунтаймах. Без ложных срабатываний