PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюс
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • AI для кодаНовое
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
    Главная
    Сообщество
    WebSocket на Node.js: пишем чат и real-time уведомления

    WebSocket на Node.js: пишем чат и real-time уведомления

    Аватар автора WebSocket на Node.js: пишем чат и real-time уведомления

    Антон Ларичев

    Иконка календаря09 июня 2026
    WebSocketNode.jsReal-timeBackendmiddleИконка уровня middle
    Картинка поста WebSocket на Node.js: пишем чат и real-time уведомления

    Введение

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

    В статье разберём, как поднять WebSocket-сервер на Node.js с помощью библиотеки ws, написать минимальный чат с комнатами, добавить heartbeat-проверку живых соединений и реализовать broadcast-уведомления. Все примеры рассчитаны на Node.js 20+ и работают без дополнительных фреймворков.

    Как устроен WebSocket

    Клиент инициирует обычный HTTP-запрос с заголовком Upgrade: websocket. Если сервер согласен, он отвечает кодом 101 Switching Protocols, после чего TCP-соединение переключается в режим обмена бинарными фреймами. Дальше обе стороны могут отправлять данные в любой момент — никакого polling.

    Важные моменты, о которых часто забывают:

    • WebSocket не имеет встроенного авто-реконнекта — это задача клиента.
    • Прокси и балансировщики могут резать idle-соединения через 60 секунд.
    • Браузерный WebSocket не передаёт кастомные заголовки, поэтому токен авторизации обычно прокидывают через query-параметр или subprotocol.

    Базовый сервер на ws

    Установим библиотеку: npm install ws. Затем создадим простой сервер, который принимает соединения и отвечает эхо-сообщением.

    import { WebSocketServer } from 'ws';
    
    // Создаём WebSocket-сервер на порту 8080
    const wss = new WebSocketServer({ port: 8080 });
    
    wss.on('connection', (ws, req) => {
      // Логируем IP подключившегося клиента
      console.log('Новое подключение:', req.socket.remoteAddress);
    
      ws.on('message', (data) => {
        // data приходит как Buffer, приводим к строке
        const text = data.toString();
        console.log('Получено:', text);
        ws.send(`echo: ${text}`);
      });
    
      ws.on('close', () => {
        console.log('Клиент отключился');
      });
    });
    

    Это рабочий сервер, но он держит соединения вечно и не умеет рассылать сообщения нескольким клиентам. Перейдём к чату.

    Чат с комнатами

    Реализуем простой чат, в котором клиент при подключении указывает комнату через query-параметр, а сервер раздаёт сообщения только участникам этой комнаты.

    import { WebSocketServer } from 'ws';
    import { parse } from 'url';
    
    const wss = new WebSocketServer({ port: 8080 });
    
    // Храним соединения по комнатам: Map<roomId, Set<WebSocket>>
    const rooms = new Map();
    
    wss.on('connection', (ws, req) => {
      const { query } = parse(req.url, true);
      const room = query.room || 'general';
    
      // Регистрируем клиента в комнате
      if (!rooms.has(room)) rooms.set(room, new Set());
      rooms.get(room).add(ws);
      ws.room = room;
    
      ws.on('message', (raw) => {
        const message = raw.toString();
        // Рассылаем сообщение всем, кроме отправителя
        for (const client of rooms.get(room)) {
          if (client !== ws && client.readyState === ws.OPEN) {
            client.send(JSON.stringify({ room, message }));
          }
        }
      });
    
      ws.on('close', () => {
        // Убираем клиента и чистим пустую комнату
        rooms.get(room).delete(ws);
        if (rooms.get(room).size === 0) rooms.delete(room);
      });
    });
    

    Клиент подключается строкой new WebSocket('ws://localhost:8080?room=js-news'). Такой подход масштабируется на тысячи соединений в одном Node.js-процессе, а для горизонтального масштабирования между инстансами обычно подключают Redis Pub/Sub.

    Heartbeat и обнаружение мёртвых соединений

    TCP-соединение может «зависнуть» — клиент потерял сеть, но сервер ещё не знает об этом. Решается это ping/pong-фреймами на уровне протокола.

    function heartbeat() {
      // Помечаем соединение как живое при получении pong
      this.isAlive = true;
    }
    
    wss.on('connection', (ws) => {
      ws.isAlive = true;
      ws.on('pong', heartbeat);
    });
    
    // Каждые 30 секунд проверяем все соединения
    const interval = setInterval(() => {
      for (const ws of wss.clients) {
        if (ws.isAlive === false) {
          // Клиент не ответил на прошлый ping — закрываем
          ws.terminate();
          continue;
        }
        ws.isAlive = false;
        ws.ping();
      }
    }, 30000);
    
    wss.on('close', () => clearInterval(interval));
    

    Этот паттерн — стандарт де-факто из документации ws. Без него у вас постепенно накопятся «зомби»-соединения, и сервер начнёт жрать память.

    Real-time уведомления

    Для уведомлений нужен broadcast по конкретному пользователю, а не по комнате. Привяжем сокет к userId после авторизации и сделаем функцию отправки.

    const userSockets = new Map(); // userId -> Set<WebSocket>
    
    wss.on('connection', (ws, req) => {
      const { query } = parse(req.url, true);
      // Проверяем JWT-токен, выдаём userId
      const userId = verifyToken(query.token);
      if (!userId) return ws.close(4001, 'Unauthorized');
    
      if (!userSockets.has(userId)) userSockets.set(userId, new Set());
      userSockets.get(userId).add(ws);
    
      ws.on('close', () => userSockets.get(userId).delete(ws));
    });
    
    // Вызываем из любого места приложения
    export function notify(userId, payload) {
      const sockets = userSockets.get(userId);
      if (!sockets) return;
      const data = JSON.stringify(payload);
      for (const ws of sockets) {
        if (ws.readyState === ws.OPEN) ws.send(data);
      }
    }
    

    Теперь любой сервис (создание заказа, новое сообщение, изменение статуса) может позвать notify(userId, {...}) и пользователь моментально увидит событие в UI.

    Частые ошибки

    • Передача токена в заголовке. Браузер не умеет добавлять Authorization к WebSocket-запросу. Используйте query-параметр или subprotocol и сразу закрывайте соединение при невалидном токене.
    • Отсутствие heartbeat. Без ping/pong зависшие клиенты копятся часами и съедают память.
    • Broadcast без проверки readyState. Если отправить в закрывающийся сокет, получите исключение и уроните весь цикл рассылки.
    • Хранение state только в памяти процесса. При перезапуске или нескольких инстансах за балансировщиком сообщения теряются. Решение — Redis Pub/Sub или отдельный сервис состояния.
    • Игнорирование размера сообщения. По умолчанию ws принимает фреймы до 100 МБ. Установите лимит через опцию maxPayload, иначе один клиент уронит сервер.

    Заключение

    WebSocket на Node.js закрывает почти весь спектр real-time задач: от чатов до биржевых лент. Библиотека ws даёт минималистичный, но достаточный API, а связка с heartbeat и Redis Pub/Sub позволяет дорасти до продакшена с десятками тысяч одновременных соединений. Начните с простого эхо-сервера, добавьте комнаты, прикрутите авторизацию и мониторинг живых соединений — и у вас будет полноценная real-time платформа без тяжёлых фреймворков.

    Иконка глаза513

    Комментарии

    0

    Постройте личный план изучения Nuxt - fullstack Vue фреймворк до уровня Middle — бесплатно!

    Nuxt - fullstack Vue фреймворк — часть карты развития Frontend

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Основы разработки

    Антон Ларичев
    Гарантия
    Бонусы
    иконка звёздочки рейтинга5.0
    бесплатно
    Подробнее
    изображение курса

    Angular

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.5
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Feature-Sliced Design

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.5
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста Как установить Node.js на Windows: пошаговая инструкция
    Иконка аватараАнтон
    Иконка календаря17 июля 2026
    Node.jsWindowsnpm+ 2juniorИконка уровня junior

    Как установить Node.js на Windows: пошаговая инструкция

    Как установить Node.js на Windows — пошаговая инструкция через официальный установщик и nvm-windows с разбором частых ошибок.

    Иконка чипа0
    Иконка глаза195
    Иконка комментариев0
    Картинка поста CI/CD с GitHub Actions: автоматизация деплоя для начинающих
    Иконка аватараАнтон
    Иконка календаря03 июля 2026
    CI/CDGitHub ActionsDevOps+ 2juniorИконка уровня junior

    CI/CD с GitHub Actions: автоматизация деплоя для начинающих

    CI/CD с GitHub Actions: пошаговая настройка pipeline для автоматического тестирования и деплоя Node.js-приложений без сторонних сервисов.

    Иконка чипа0
    Иконка глаза199
    Иконка комментариев0
    Картинка поста JWT токены: структура, работа и аутентификация в Node.js
    Иконка аватараАнтон
    Иконка календаря01 июля 2026
    JWTаутентификацияNode.js+ 2juniorИконка уровня junior

    JWT токены: структура, работа и аутентификация в Node.js

    JWT токены — стандарт для безопасной передачи данных между клиентом и сервером. Разбираем структуру, реализацию и частые ошибки.

    Иконка чипа0
    Иконка глаза209
    Иконка комментариев0
    Иконка чипа+1