Ответ
Реализация WebSocket-сервера строится на нескольких ключевых принципах, обеспечивающих двустороннюю, низколатентную связь.
Основные принципы:
-
Handshake (рукопожатие) через HTTP.
- Соединение начинается с обычного HTTP-запроса
GETс заголовкамиUpgrade: websocketиConnection: Upgrade. - Сервер отвечает статусом
101 Switching Protocols, подтверждая переход на протокол WebSocket. - Пример заголовков клиента:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
- Соединение начинается с обычного HTTP-запроса
-
Фреймовая структура данных.
- После рукопожатия данные передаются не потоком байт, а структурированными фреймами.
- Каждый фрейм содержит заголовок (управляющая информация: тип фрейма, маскировка, длина) и полезную нагрузку (payload).
- Типы фреймов: текстовый (
0x1), бинарный (0x2), закрытие соединения (0x8), ping (0x9), pong (0xA).
-
Поддержка состояния (Stateful).
- В отличие от HTTP, WebSocket-соединение является долгоживущим и сохраняет состояние между обменами сообщениями. Сервер должен отслеживать все активные соединения.
-
Контроль за соединением (Ping/Pong).
- Для проверки живости соединения используются служебные фреймы Ping и Pong. Сервер или клиент может отправить Ping и ожидать Pong в ответ.
-
Безопасное закрытие.
- Закрытие инициируется фреймом типа
Close(0x8), который может содержать код причины и описание. Вторая сторона должна ответить таким же фреймом.
- Закрытие инициируется фреймом типа
Практический пример на Node.js с библиотекой ws:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// Принцип 1 & 3: Обработка рукопожатия и хранение состояния
wss.on('connection', function connection(ws) {
console.log('Новое соединение установлено');
// Принцип 2: Обработка входящих сообщений (фреймов)
ws.on('message', function incoming(message) {
console.log('Получено: %s', message);
// Широковещательная рассылка всем подключенным клиентам
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
// Принцип 5: Обработка закрытия соединения
ws.on('close', function close() {
console.log('Соединение закрыто');
});
});