Введение в WebSocket на JavaScript: что такое real-time связь?
В современном вебе пользователи привыкли к мгновенным обновлениям: сообщения в чатах приходят без перезагрузки страницы, котировки акций меняются в реальном времени, а в онлайн-играх действия соперников видны сразу. Всё это стало возможным благодаря технологии WebSocket — протоколу, который обеспечивает постоянное двустороннее соединение между клиентом и сервером.
В отличие от классического HTTP-запроса, где клиент отправляет запрос и ждёт ответ, WebSocket позволяет серверу самостоятельно отправлять данные клиенту в любой момент. Это открывает безграничные возможности для создания real-time приложений на JavaScript. В этой статье мы разберём WebSocket от основ до создания полноценного чата на Node.js.
Основы WebSocket: как работает протокол
Что такое WebSocket и чем он отличается от HTTP?
WebSocket — это протокол связи, работающий поверх TCP. Он начинается как HTTP-запрос (рукопожатие), после чего соединение "апгрейдится" до WebSocket, и данные передаются в обоих направлениях без дополнительных заголовков. Это делает его идеальным для приложений, где важна низкая задержка.
Основные преимущества WebSocket перед традиционными методами (Polling, Long Polling):
- Постоянное соединение — не нужно создавать новый HTTP-запрос для каждого обновления
- Низкая задержка — данные передаются мгновенно, как только появляются на сервере
- Экономия трафика — минимальный размер фреймов (всего 2 байта служебных данных)
- Двусторонняя связь — сервер может инициировать отправку данных
Установка соединения: рукопожатие
Процесс начинается с HTTP-запроса от клиента, содержащего заголовок Upgrade: websocket. Сервер, поддерживающий WebSocket, отвечает статусом 101 Switching Protocols. После этого соединение считается установленным.
// Пример HTTP-запроса на установку WebSocket
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Создание WebSocket сервера на Node.js
Установка и настройка ws-библиотеки
Для работы с WebSocket на серверной стороне мы будем использовать популярную библиотеку ws. Она легковесна, быстра и полностью соответствует спецификации протокола.
// Установка библиотеки
// npm install ws
Создадим простой WebSocket-сервер:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('Новый клиент подключился');
ws.on('message', (message) => {
console.log('Получено сообщение:', message.toString());
// Отправляем ответ всем подключённым клиентам
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(`Эхо: ${message}`);
}
});
});
ws.send('Добро пожаловать на WebSocket сервер!');
});
console.log('WebSocket сервер запущен на порту 8080');
Подключение клиента на JavaScript
На стороне клиента (браузер) используется встроенный объект WebSocket:
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = () => {
console.log('Соединение установлено');
socket.send('Привет, сервер!');
};
socket.onmessage = (event) => {
console.log('Сообщение от сервера:', event.data);
};
socket.onclose = () => {
console.log('Соединение закрыто');
};
socket.onerror = (error) => {
console.error('Ошибка WebSocket:', error);
};
Создание real-time чата на JavaScript и Node.js
Архитектура приложения
Мы построим полноценный чат с поддержкой множества пользователей. Каждое сообщение будет транслироваться всем подключённым клиентам. Для идентификации пользователей добавим простую систему имён.
Серверная часть чата
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3000 });
const clients = new Map(); // Храним id клиента и его имя
wss.on('connection', (ws) => {
const clientId = Date.now();
clients.set(ws, { id: clientId, name: `User_${clientId}` });
// Уведомляем всех о новом пользователе
broadcast({ type: 'system', message: `${clients.get(ws).name} присоединился к чату` }, ws);
ws.on('message', (data) => {
const parsed = JSON.parse(data);
if (parsed.type === 'setName') {
clients.get(ws).name = parsed.name;
broadcast({ type: 'system', message: `Пользователь сменил имя на ${parsed.name}` });
} else if (parsed.type === 'message') {
const sender = clients.get(ws).name;
broadcast({ type: 'message', sender, text: parsed.text, time: new Date().toLocaleTimeString() });
}
});
ws.on('close', () => {
const user = clients.get(ws);
broadcast({ type: 'system', message: `${user.name} покинул чат` });
clients.delete(ws);
});
});
function broadcast(data, excludeWs = null) {
wss.clients.forEach((client) => {
if (client !== excludeWs && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
});
}
console.log('Чат-сервер запущен на ws://localhost:3000');
Клиентская часть чата (HTML + JavaScript)
<!DOCTYPE html>
<html>
<head>
<title>Real-time Чат</title>
</head>
<body>
<div id="messages"></div>
<input type="text" id="nameInput" placeholder="Ваше имя" />
<input type="text" id="msgInput" placeholder="Сообщение" />
<button onclick="sendMessage()">Отправить</button>
<script>
const socket = new WebSocket('ws://localhost:3000');
const messagesDiv = document.getElementById('messages');
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
const msgElement