WebSocket

Материал из llmdocs — справочник разработчика
WebSocket
СтандартRFC 6455 (декабрь 2011)
API браузераWHATWG WebSockets Standard
Схемы URIws:// (порт 80), wss:// (порт 443, поверх TLS)
Работает поверхTCP; для HTTP/2 — RFC 8441, для HTTP/3 — RFC 9220
УстановкаHTTP Upgrade (код 101)
АльтернативыServer-Sent Events, long polling, WebTransport

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

История

До WebSocket «живые» веб-приложения обходились приёмами вроде частого опроса (polling), долгого опроса (long polling) и Comet — все они расходовали лишние запросы и заголовки. Протокол появился в рамках HTML5 в 2008–2010 годах, после нескольких несовместимых черновиков был стандартизирован IETF как RFC 6455 в 2011 году.[1] Браузерный интерфейс WebSocket поддерживает WHATWG.[2] Сегодня его поддерживают все браузеры и серверные платформы.

Установка соединения

Соединение начинается с обычного HTTP/1.1-запроса с просьбой «сменить протокол»:

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: https://example.com

Сервер отвечает кодом 101 Switching Protocols и заголовком Sec-WebSocket-Accept — хешем SHA-1 от присланного ключа с фиксированной строкой. Это не защита, а проверка, что сервер действительно понимает WebSocket. После этого то же TCP-соединение используется для кадров WebSocket. Для HTTP/2 и HTTP/3 определены свои способы открыть WebSocket внутри потока (RFC 8441, RFC 9220).[4]

Формат кадров

Данные передаются кадрами (frames) с компактным заголовком от 2 до 14 байт:

Поле Назначение
FIN последний ли это фрагмент сообщения
Opcode тип: текст (UTF-8), двоичные данные, продолжение, close, ping, pong
Mask маскирован ли полезный груз (клиент → сервер — всегда)
Payload length длина: 7 бит, 16 или 64 бита

Маскирование кадров от клиента случайным ключом защищает промежуточные прокси от атак отравления кэша. Управляющие кадры ping/pong проверяют, что соединение живо, а close с кодом причины (например, 1000 — нормальное закрытие, 1001 — уход со страницы) завершает его корректно. Расширение permessage-deflate сжимает сообщения.[5]

API в браузере

const ws = new WebSocket("wss://example.com/chat");

ws.addEventListener("open", () => ws.send(JSON.stringify({type: "hello"})));
ws.addEventListener("message", (event) => {
  const msg = JSON.parse(event.data);
  console.log("получено:", msg);
});
ws.addEventListener("close", (e) => console.log("закрыто", e.code));

Обмен обычно ведут в формате JSON. API не умеет само переподключаться — это делает приложение, с экспоненциальной задержкой между попытками.[3]

Сервер

На сервере WebSocket поддерживают все основные стеки: ws и Socket.IO в Node.js, websockets и Starlette в Python (FastAPI), gorilla/websocket и nhooyr.io/websocket в Go, tokio-tungstenite в Rust. Socket.IO — не сам WebSocket, а надстройка со своим протоколом: комнаты, подтверждения, автоматическое переподключение и откат на long polling.

Каждое соединение долгоживущее, поэтому серверу нужна асинхронная модель ввода-вывода, а при нескольких экземплярах — общая шина сообщений (Redis Pub/Sub, NATS), чтобы событие с одного сервера дошло до клиентов, подключённых к другому.

За прокси и балансировщиком

Обратный прокси должен пробрасывать заголовки Upgrade и Connection и не закрывать неактивные соединения слишком рано. Для nginx:[6]

location /ws/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 1h;
}

При балансировке важно, что соединение закреплено за одним сервером на всё время жизни; алгоритм least connections распределяет такие соединения лучше, чем round robin.

Безопасность

  • Используйте wss:// — WebSocket поверх TLS; незашифрованный ws:// легко перехватить и блокируется многими сетями.
  • Политика same-origin на WebSocket не распространяется: сервер обязан проверять заголовок Origin, иначе возможна атака Cross-Site WebSocket Hijacking.
  • Аутентификацию выполняют при установке (cookie, токен в первом сообщении); токен в URL попадает в логи.
  • Ограничивайте размер сообщений и частоту — открытое соединение легко превратить в канал перегрузки.

Альтернативы

  • Server-Sent Events (SSE) — односторонний поток событий от сервера по обычному HTTP; проще, работает через любые прокси, переподключается сам. Именно SSE используют многие API языковых моделей для потокового вывода.
  • Long polling — запасной вариант для очень старых сред.
  • WebTransport — новый API поверх HTTP/3 и QUIC с несколькими потоками и ненадёжными датаграммами.

См. также

Источники

  1. RFC 6455 — The WebSocket Protocol
  2. WHATWG — WebSockets Standard
  3. MDN — The WebSocket API
  4. RFC 8441 — Bootstrapping WebSockets with HTTP/2
  5. RFC 7692 — Compression Extensions for WebSocket
  6. nginx — WebSocket proxying