awesome-everything EN
↑ Обратно к восхождению

Сети и протоколы

WebSocket: HTTP-апгрейд до постоянного соединения

Суть Как обычный HTTP-запрос превращается в двусторонний канал сообщений за один round-trip — и что каждый заголовок апгрейда означает на практике.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на junior-высоте — поверхность
◷ 10 min

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

Проблема, которую HTTP не решает

HTTP — это request-response: клиент спрашивает, сервер отвечает, разговор окончен. Для живого чата, котировок, многопользовательских игр, совместного редактирования — всего, где сервер должен пушить данные без ожидания нового запроса — HTTP структурно не подходит. Каждый HTTP-запрос добавляет минимум один полный round-trip задержки прежде, чем данные могут потечь обратно.

WebSocket решает это, превращая существующее HTTP-соединение в постоянный двусторонний канал, где любая сторона отправляет в любой момент.

Метафора

С HTTP вы отправляете письма: пишете, посылаете, ждёте ответа. С WebSocket поднимаете трубку: как только соединились, обе стороны говорят свободно, не ожидая очереди.

Рукопожатие апгрейда шаг за шагом

Соединение начинается как обычный HTTP/1.1-запрос. Клиент сигнализирует, что хочет переключить протокол:

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

Каждый заголовок несёт конкретный смысл:

  • Upgrade: websocket — объявляет целевой протокол.
  • Connection: Upgrade — сообщает промежуточным прокси, что это смена протокола, а не обычный keepalive.
  • Sec-WebSocket-Key — 16 случайных байт в base64. Сервер использует это, чтобы доказать, что намеренно обработал апгрейд (а не отдал кэшированный ответ — см. врезку ниже).
  • Sec-WebSocket-Version: 13 — указывает RFC 6455, единственную версию на практике.

Сервер проверяет запрос (метод GET, HTTP/1.1, корректные заголовки) и отвечает:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Значение Sec-WebSocket-Accept вычисляется как:

base64(SHA-1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))

Фиксированная строка (258EAFA5-…) — это magic GUID из RFC. После ответа 101 обе стороны перестают говорить HTTP. TCP-соединение теперь является чистым WebSocket-потоком.

Факты о WebSocket-рукопожатии
Дополнительные round-trips для апгрейда
0 (использует существующее HTTP-соединение)
HTTP-код при успешном апгрейде
101 Switching Protocols
RFC, определяющий WebSocket
RFC 6455 (2011)
Sec-WebSocket-Version в продакшне
13 (единственная используемая)
Overhead заголовка фрейма (маленький сервер→клиент)
2 байта
Задержка сообщения после рукопожатия
0–5 мс

Сценарий от начала до конца

Вы открываете Discord в браузере:

  1. Браузер делает TCP + TLS к gateway.discord.gg.
  2. Браузер отправляет HTTP Upgrade-запрос выше.
  3. Сервер отвечает 101. Обе стороны прекращают HTTP-парсинг.
  4. С этого момента сервер Discord пушит фреймы с сообщениями в браузер в момент, когда друг что-то отправил. Браузер отправляет фреймы (индикаторы набора, сообщения) в другую сторону без открытия нового соединения.
  5. Когда вы закрываете вкладку, обмениваются close-фреймами и TCP-соединение завершается.
Почему это работает

Почему вычисление Sec-WebSocket-Accept защищает от cache-poisoning. В ранние дни веба вредоносный JavaScript на site-a.com мог открыть TCP-соединение к промежуточному прокси и отправить байты, похожие на валидный HTTP-ответ. Если прокси кэшировал их, другие пользователи могли получить вредоносный контент. Связка Sec-WebSocket-Key + GUID + SHA-1 делает практически невозможным подделку заголовка Accept без знания точного ключа, который сгенерирует сервер — прокси не может закэшировать результат вычисления, которого сам не проводил.

Викторина

Зачем WebSocket требует HTTP Upgrade-запроса, а не просто открывает сырое TCP-соединение на новый порт?

Викторина

После завершения WebSocket-апгрейда с ответом 101 Switching Protocols — для чего используется протокол HTTP?

Расставь шаги по порядку

Упорядочите шаги WebSocket-рукопожатия:

  1. 1 Клиент отправляет HTTP GET с Upgrade: websocket и Sec-WebSocket-Key
  2. 2 Сервер проверяет запрос и вычисляет Sec-WebSocket-Accept
  3. 3 Сервер отвечает 101 Switching Protocols с заголовком Accept
  4. 4 Обе стороны прекращают HTTP; соединение становится двусторонним WebSocket
  5. 5 Любая сторона теперь может отправлять фреймы в любой момент
Закончи аналогию

Заполните пропуск: WebSocket — это как _______, где обе стороны могут говорить свободно, не ждя очереди.

Вспомните перед уходом
  1. 01
    В одном предложении: почему сервер не может просто слать повторяющиеся HTTP-запросы, чтобы пушить данные клиенту?
  2. 02
    Что сигнализирует ответ 101 Switching Protocols и что происходит на проводе сразу после?
  3. 03
    Зачем Sec-WebSocket-Key добавляют к фиксированному GUID перед хешированием, а не хешируют напрямую?
Итог

HTTP — это только pull: каждая доставка данных требует, чтобы клиент сначала спросил. WebSocket снимает это ограничение, апгрейдя обычное HTTP/1.1-соединение до полнодуплексного канала. Апгрейд требует ровно одного дополнительного HTTP round-trip: GET с Upgrade: websocket и случайным Sec-WebSocket-Key, в ответ — 101 Switching Protocols и вычисленный через SHA-1 Sec-WebSocket-Accept. После ответа 101 HTTP не используется — обе стороны обмениваются компактными бинарными фреймами, причём любая сторона может инициировать. Весь WebSocket-handshake не добавляет ни одного TCP-соединения и ни одной TLS-сессии. Задержка сообщения после рукопожатия составляет 0–5 мс, ограниченная только сетевым RTT.

Связанные уроки
встречается в258
Продолжить восхождение ↑Формат WebSocket-фрейма: opcodes, маскирование, фрагментация
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.