WebSocket сервер: ws, Socket.io
WebSocket — протокол полнодуплексной связи через одно постоянное TCP-соединение; ws — минималистичная Node.js реализация, Socket.io — библиотека поверх WebSocket с автоматическим fallback, комнатами и событийной моделью.
Зачем нужно
HTTP — протокол запрос/ответ: сервер не может инициировать отправку данных клиенту. WebSocket открывает постоянное двустороннее соединение, что идеально для real-time приложений: чаты, live-уведомления, совместное редактирование, биржевые котировки, мультиплеер.
Где используется
- Чаты и мессенджеры
- Live-уведомления (push-уведомления без polling)
- Совместное редактирование документов (Google Docs-стиль)
- Онлайн-игры (мультиплеер)
- Биржевые терминалы и дашборды с обновлениями в реальном времени
Основной контент
ws — минималистичный WebSocket сервер
npm install ws
// server.js
const { WebSocketServer } = require('ws');
const http = require('http');
const express = require('express');
const app = express;
const server = http.createServer(app);
const wss = new WebSocketServer({ server });
// Хранилище клиентов
const clients = new Map();
wss.on('connection', (ws, req) => {
const clientId = Date.now();
clients.set(clientId, ws);
console.log(`Client ${clientId} connected. Total: ${clients.size}`);
// Получить сообщение
ws.on('message', (data) => {
const message = JSON.parse(data.toString());
console.log(`Received from ${clientId}:`, message);
// Broadcast всем клиентам
for (const [id, client] of clients) {
if (client.readyState === ws.OPEN) {
client.send(JSON.stringify({ from: clientId, ...message }));
}
}
});
// Клиент отключился
ws.on('close', () => {
clients.delete(clientId);
console.log(`Client ${clientId} disconnected`);
});
ws.on('error', (err) => console.error(`Client ${clientId} error:`, err));
// Отправить приветствие
ws.send(JSON.stringify({ type: 'connected', clientId }));
});
server.listen(3000, () => console.log('Server with WebSocket on port 3000'));
// Клиент (браузер)
const ws = new WebSocket('ws://localhost:3000');
ws.onmessage = (event) => console.log(JSON.parse(event.data));
ws.onopen = () => ws.send(JSON.stringify({ type: 'chat', text: 'Hello!' }));
Socket.io — события, комнаты, broadcast
npm install socket.io
// server.js
const { Server } = require('socket.io');
const express = require('express');
const http = require('http');
const app = express;
const server = http.createServer(app);
const io = new Server(server, {
cors: { origin: 'http://localhost:3000', methods: ['GET', 'POST'] }
});
io.on('connection', (socket) => {
console.log(`User connected: ${socket.id}`);
// Войти в комнату
socket.on('join-room', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('user-joined', { userId: socket.id });
});
// Сообщение в чат (в комнату)
socket.on('chat-message', ({ roomId, text }) => {
io.to(roomId).emit('chat-message', {
from: socket.id,
text,
timestamp: new Date
});
});
// Broadcast всем кроме отправителя
socket.on('typing', (data) => {
socket.broadcast.emit('user-typing', { userId: socket.id });
});
socket.on('disconnect', () => {
console.log(`User disconnected: ${socket.id}`);
});
});
server.listen(3000);
// Клиент (browser)
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000');
socket.emit('join-room', 'room-42');
socket.on('chat-message', (msg) => console.log(msg));
socket.emit('chat-message', { roomId: 'room-42', text: 'Hello!' });
Масштабирование — Socket.io с Redis adapter
npm install @socket.io/redis-adapter ioredis
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate;
await Promise.all([pubClient.connect, subClient.connect]);
io.adapter(createAdapter(pubClient, subClient));
// Теперь несколько Node.js-процессов синхронизируют события через Redis
Частые ошибки
- Не закрывать соединение при ошибке — зависшие соединения потребляют память; настроить
ws.terminateпо таймауту - Хранить состояние в памяти сервера — при горизонтальном масштабировании клиент может попасть на другой инстанс; использовать Redis adapter
- Не валидировать данные через WebSocket — входящие данные могут быть любыми; всегда парсить и валидировать как JSON
- Использовать Socket.io там где достаточно ws — Socket.io добавляет 40+ KB клиентской библиотеки; для простых случаев нативного WebSocket API хватает
Связанные темы
Ресурсы
🎓 Источник: WebSocket сервер на Node.js (электронные таблицы и чат)
- 📅 2018-10-31 · YouTube
- Тезисы:
- URL-схема
ws://илиwss://(secure); путь можно использовать как комнату. - WebSocket-библиотека пропатчивает экземпляр
http.Server— добавляет поддержку HTTPUpgrade. autoAcceptConnections: false→ ловимrequestи явно вызываемrequest.accept.- Сообщения приходят с типом:
utf8Data(текст) илиbinaryData(Buffer). - Список подключений храним для broadcast; рассылка всем кроме отправителя — паттерн чата.
- URL-схема
- Цитата: «Одна библиотека может влезть в кишки другой — WebSocket пропатчивает HTTP-сервер, чтобы понимать HTTP Upgrade.»
🎓 Источник: Ревью кода Websocket на JavaScript для Node.js (metacom)
- 📅 2025-08-30 · YouTube
- Тезисы:
- Транспорт WebSocket прозрачен для API: тот же контракт что HTTP, только двусторонний.
- Бинарный фрейм быстрее текстового; для JSON важна сериализация структурированно.
ws.ping/pongдля keep-alive; зависшие соединения отрезатьterminateпо таймауту.