Socket.io: обзор

Socket.io — библиотека для двусторонней коммуникации в реальном времени, надстраивающая WebSocket с автоматическим fallback, комнатами и событийной моделью.

Зачем нужно

Нативный WebSocket требует ручного управления переподключением, нет встроенных комнат и namespace. Socket.io добавляет: автоматическое переподключение, fallback на long polling при недоступности WS, именованные события, комнаты для групповой рассылки и поддержку нескольких транспортов. Идеален для чатов, коллаборативных инструментов и многопользовательских игр.

Где используется

  • Чаты и мессенджеры (комнаты, группы)
  • Совместное редактирование (Google Docs-подобные функции)
  • Игры в реальном времени (синхронизация состояния)
  • Live-дэшборды и мониторинг

Сервер (Node.js)

// npm install socket.io
const { createServer } = require('http');
const { Server } = require('socket.io');
const express = require('express');

const app = express;
const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: { origin: 'http://localhost:3000', methods: ['GET', 'POST'] },
});

io.on('connection', (socket) => {
  console.log('Подключён:', socket.id);

  // Слушаем событие от клиента
  socket.on('chat:message', ({ room, text, user }) => {
    // Рассылаем всем в комнате, включая отправителя
    io.to(room).emit('chat:message', { text, user, timestamp: Date.now() });
  });

  // Вход в комнату
  socket.on('room:join', (roomId) => {
    socket.join(roomId);
    socket.to(roomId).emit('room:user-joined', { userId: socket.id });
  });

  // Выход из комнаты
  socket.on('room:leave', (roomId) => {
    socket.leave(roomId);
    socket.to(roomId).emit('room:user-left', { userId: socket.id });
  });

  socket.on('disconnect', () => {
    console.log('Отключён:', socket.id);
  });
});

httpServer.listen(4000);

Клиент (браузер)

// npm install socket.io-client
import { io } from 'socket.io-client';

const socket = io('http://localhost:4000', {
  auth: { token: localStorage.getItem('token') },
  reconnection: true,
  reconnectionDelay: 1000,
  reconnectionAttempts: 5,
});

// Отправка события
socket.emit('room:join', 'general');
socket.emit('chat:message', {
  room: 'general',
  text: 'Привет!',
  user: 'Антон',
});

// Приём событий
socket.on('chat:message', ({ text, user, timestamp }) => {
  addMessageToUI({ text, user, timestamp });
});

socket.on('room:user-joined', ({ userId }) => {
  showNotification(`${userId} вошёл в комнату`);
});

// Управление подключением
socket.on('connect', () => console.log('Подключён:', socket.id));
socket.on('disconnect', reason => console.log('Отключён:', reason));
socket.on('connect_error', err => console.error('Ошибка:', err.message));

// Закрыть соединение
function cleanup() { socket.disconnect(); }

Комнаты и Namespace

// Сервер: namespace — логическая группа соединений
const adminNs = io.of('/admin');
adminNs.on('connection', socket => { /* только для /admin */ });

// Клиент: подключение к namespace
const adminSocket = io('http://localhost:4000/admin');

// Rooms — подмножества соединений в namespace
// broadcast в комнату (кроме отправителя)
socket.to('room-id').emit('event', data);

// broadcast всем в комнате (включая отправителя)
io.to('room-id').emit('event', data);

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

  • Забывают настроить CORS — соединение отклоняется браузером
  • Используют Socket.io там, где достаточно SSE — однонаправленный поток не требует WebSocket
  • Не обрабатывают disconnect — утечка подписчиков и состояния комнат
  • Хранят состояние в памяти процесса при горизонтальном масштабировании — нужен Redis adapter

Связанные темы

Ресурсы