WebSocket для полнодуплексной связи

сервер HTTP браузер WebSocket
WebSocket для полнодуплексной связи

1. Что такое веб-сокет?

WebSocket — это сетевой протокол связи. Родившийся в 2009 году, он был определен IETF в качестве стандарта связи RFC 6455 в 2011 году. А спецификация дополнена RFC7936. WebSocket API также стандартизирован W3C.

WebSocket — это метод, который HTML5 начал предоставлять через одно TCP-соединение.Протокол для полнодуплексной связи. Без концепции запроса и ответа статус полностью равен.Как только соединение установлено, оно создает истинное и постоянное соединение, и обе стороны могут отправлять данные другой стороне в любое время.

(HTML5 — это последняя версия HTML, включающая некоторые новые теги и новый API. HTTP — это протокол, а последняя версия — HTTP/2, поэтому WebSocket и HTTP имеют некоторые пересечения, и между ними по-прежнему много различий. Пересечение находится на стадии HTTP-рукопожатия, после успешного рукопожатия данные передаются напрямую из TCP-канала.)

2. Зачем изобретать WebSocket?

До того, как не было WebSocket, в Интернете были следующие решения для реализации мгновенной связи: первоначальный опрос, более поздний длительный опрос, последний метод на основе потоковой передачи и последний SSE, которые также испытали несколько различных методов эволюции.

(1) Начальный короткий этап опроса.

Таким образом, получение информации в режиме реального времени нецелесообразно, так как клиент и сервер всегда будут на связи, и время от времени их будут спрашивать. Клиент будет опрашивать новые сообщения. Таким образом, будет много соединений, одно для приема и одно для отправки. И каждый раз, когда отправляется запрос, будет HTTP-заголовок, который будет потреблять много трафика и загружать ЦП.

На этом этапе вы можете видеть, что Запрос соответствует Ответу, и он возвращается туда и обратно.

На веб-стороне короткий опрос реализован с использованием AJAX JSONP Polling.

Поскольку HTTP не может поддерживать соединения на неопределенный срок, он не может часто передавать данные между сервером и веб-браузером в течение длительного времени, поэтому веб-приложения реализуют циклический перебор, выполняя частые асинхронные запросы JavaScript и XML (AJAX).

  • Достоинства: короткое подключение, простая серверная обработка, поддержка кроссдоменности, хорошая браузерная совместимость.
  • Недостатки: есть определенная задержка, нагрузка на сервер высока, пропускная способность тратится впустую, и большинство из них являются недействительными запросами.

(2) Улучшенный этап длительного опроса Длительный опрос (Comet Long polling)

Длинный опрос — это улучшенная версия опроса.После того, как клиент отправляет HTTP на сервер, нового сообщения нет.Если нет нового сообщения, он будет ждать. Он не будет возвращен клиенту до тех пор, пока не появится сообщение или тайм-аут. После возврата сообщения клиент снова устанавливает соединение и так далее. Этот подход в некоторой степени уменьшает такие проблемы, как пропускная способность сети и загрузка ЦП.

Этот метод также имеет определенные недостатки, и производительность в реальном времени невысока. Если это система с высоким уровнем реального времени, этот метод точно не будет использоваться. Поскольку запрос GET требует 2 RTT туда и обратно, вполне вероятно, что за этот период времени данные сильно изменились, а данные, полученные клиентом, сильно задержались.

Кроме того, проблема низкого использования пропускной способности сети не была решена изначально. Каждый запрос будет иметь один и тот же заголовок.

Соответственно, в Интернете также есть длинный опрос AJAX, также называемый длинным опросом XHR.

Клиент открывает запрос AJAX к серверу, а затем ждет ответа. Серверу нужны определенные функции, позволяющие приостановить запрос. Как только произойдет событие, сервер отправит ответ в приостановленном запросе и закройте запрос спрашивайте. После обработки информации, возвращенной сервером, клиент снова отправляет запрос на повторное установление соединения и так далее.

  • Преимущества: сокращение времени опроса, низкая задержка, лучшая совместимость с браузерами.
  • Недостаток: сервер должен поддерживать много соединений.

(3) На основе потоковой передачи (кометной потоковой передачи)

1. Потоковая передача на основе Iframe и html-файла (Iframe Streaming)

Метод потоковой передачи iframe заключается в том, чтобы вставить скрытый iframe на страницу, использовать его атрибут src для создания длинной связи между сервером и клиентом, и сервер передает данные (обычно HTML, с JavaScript, отвечающим за вставку информации) в iframe, на страницу обновления в реальном времени. Преимущество метода потоковой передачи iframe в том, что браузер совместим.

Использование iframe для запроса длительного соединения имеет очевидный недостаток: индикатор выполнения в нижней части IE и Morzilla Firefox будет показывать, что загрузка не завершена, а значок над IE будет продолжать вращаться, показывая, что идет загрузка.

Гении из Google решили проблему отображения загрузки в IE с помощью ActiveX под названием «htmlfile» и использовали этот метод в продукте gmail+gtalk. Алекс Рассел описывает этот подход в статье «Что еще скрыто в недрах удивительного JavaScript от Google?». Комета-iframe.tar.gz, предоставленная веб-сайтом Zeitoun, инкапсулирует объект кометы JavaScript на основе iframe и htmlfile, который поддерживает браузеры IE и Mozilla Firefox и может использоваться в качестве эталона.

  • Преимущества: простота реализации, доступность во всех браузерах, поддерживающих iframe, одно клиентское соединение, несколько отправок на сервер.
  • Недостатки: Невозможно точно узнать статус соединения.Во время запроса iframe браузера IE заголовок браузера всегда находится в состоянии загрузки, а нижняя строка состояния также показывает, что он загружается.Плохой пользовательский интерфейс (htmlfile динамически записывает в память через ActiveXObject для решения этой проблемы).

2. Многокомпонентная потоковая передача AJAX (потоковая передача XHR)

Идея реализации: браузер должен поддерживать флаг multi-part, клиент отправляет запрос через AJAX, сервер поддерживает соединение, а затем может непрерывно передавать данные клиенту через механизм кодирования по частям HTTP1.1 (кодирование по частям передачи) , до истечения времени ожидания или отключения вручную.

  • Преимущества: клиент подключается один раз, а данные сервера можно передавать несколько раз.
  • Недостаток: не все браузеры поддерживают флаг составных частей.

3. Гнездо для флэш-памяти (потоковая передача флэш-памяти)

Идея реализации: встроить программу Flash с помощью класса Socket на страницу, JavaScript взаимодействует с интерфейсом Socket на стороне сервера, вызывая интерфейс Socket, предоставленный программой Flash, а JavaScript получает данные, переданные серверной стороной через Flash Socket. .

  • Плюсы: Мгновенная связь в реальном времени, а не псевдомгновенная.
  • Недостатки: клиент должен установить плагин Flash, это не HTTP-протокол и не может автоматически проходить через брандмауэр.

4. Server-Sent Events

Server Sent Events (SSE) — это также технология, объявленная HTML5, которая позволяет серверу инициировать передачу данных клиенту браузера. После создания начального соединения поток событий остается открытым до закрытия клиента. Эта технология отправляется по традиционному HTTP и имеет различные функции, которых нет в WebSockets, такие как автоматическое повторное подключение, идентификаторы событий и возможность отправки произвольных событий.

SSE использует сервер для объявления клиенту, а следующее, что нужно отправить, — это информация о потоковой передаче (streaming), которая будет отправляться непрерывно. В это время клиент не будет закрывать соединение, а всегда будет ждать новый поток данных, отправленный сервером, который можно сравнить с видеопотоком. SSE использует этот механизм для отправки информации в браузер с использованием потоковой информации. Он основан на протоколе HTTP и в настоящее время поддерживается другими браузерами, кроме IE/Edge.

SSE — это односторонний канал, и его можно отправлять только с сервера в браузер, поскольку потоковая информация — это, по сути, загрузка.

Данные SSE, отправляемые сервером в браузер, должны представлять собой текст в кодировке UTF-8 со следующей информацией заголовка HTTP.

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

Среди приведенных выше трех строк Content-Type первой строки должен указывать тип MIME как event-steam.

  • Достоинства: Подходит для частых обновлений, низкая задержка, данные пересылаются с сервера на клиент.
  • Недостатки: совместимость с браузером затруднена.

Это четыре распространенного подхода, основанного на потоке, потоковое значение IFrame, Treaking XHR, потоковое флэш-накопитель, серверные события.

С точки зрения совместимости браузера — короткий опрос/AJAX> длинный опрос/Comet> длинное соединение/SSE

Появление веб-сокетов

С точки зрения вышеупомянутых методов эволюции, это также процесс постоянного улучшения.

Короткий опрос неэффективен и тратит ресурсы (пропускную способность сети и вычислительные ресурсы). Есть определенная задержка, нагрузка на сервер высока, и большинство из них являются недействительными запросами.

Хотя длительный опрос позволяет избежать большого количества недействительных запросов, снижает нагрузку на сервер и занимает определенную полосу пропускания сети, он по-прежнему требует поддержки большого количества соединений.

Наконец, используется потоковый метод, который проталкивается от сервера к клиенту, и производительность потока в реальном времени в этом направлении лучше. Однако он по-прежнему односторонний, и клиенту, запрашивающему сервер, по-прежнему требуется HTTP-запрос.

Затем люди думают, есть ли такое идеальное решение, которое может обмениваться данными в обоих направлениях, экономить сетевые накладные расходы на заголовок запроса и иметь более сильную масштабируемость, и при этом лучше всего поддерживать такие функции, как двоичные кадры и сжатие?

Вот и придумали люди такое, казалось бы, «идеальное» решение — WebSocket.

После того, как стандарт WebSocket был опубликован в HTML5, он напрямую заменил Comet в качестве нового метода проталкивания на сервер.

Comet — это технология push-уведомлений для Интернета, которая позволяет серверу передавать обновленную информацию клиенту в режиме реального времени без запроса со стороны клиента.В настоящее время существует две реализации: длинный опрос и потоковая передача iframe.

  • преимущество:

  • Меньше накладных расходов на управление, заголовки пакетов, используемые для управления протоколом, относительно малы при обмене данными между сервером и клиентом после создания соединения. Без расширений этот заголовок имеет размер от 2 до 10 байт (в зависимости от длины пакета) для содержимого «сервер-клиент»; для содержимого «клиент-сервер» для этого заголовка требуется дополнительная 4-байтовая маска. Эти накладные расходы значительно сокращаются по сравнению с переносом полного заголовка каждый раз при HTTP-запросе.

  • Более высокая производительность в реальном времени, поскольку протокол является полнодуплексным, сервер может активно отправлять данные клиенту в любое время. По сравнению с HTTP-запросами, которые должны ждать, пока клиент инициирует запрос для ответа сервера, задержка значительно меньше; даже по сравнению с длительным опросом, таким как Comet, он может доставлять данные больше раз за короткий период времени.

  • Долгое соединение, оставайтесь на связи. В отличие от HTTP, Websocket необходимо сначала создать соединение, что делает его протоколом с отслеживанием состояния, а затем некоторая информация о состоянии может быть опущена при обмене данными. И HTTP-запросам может потребоваться нести информацию о состоянии (например, аутентификацию и т. д.) в каждом запросе.

  • Двунаправленная связь, лучшая поддержка бинарных файлов. Есть хорошая совместимость с протоколом HTTP. Порт по умолчанию также 80 и 443, а протокол HTTP используется на этапе рукопожатия, поэтому его нелегко защитить при рукопожатии, и его можно передавать через различные прокси-серверы HTTP.

  • Недостатки: Некоторые браузеры не поддерживаются (будет поддерживаться все больше и больше браузеров). Сценарий приложения: поддержка новых браузеров, не ограниченная фреймворком, высокая масштабируемость.

Кратко опишите WebSocket одним предложением:

WebSocket — это своего рода HTML5, который начали предоставлятьнезависимыйв одномTCPна связиполнодуплексная связьизсостояние(он отличается от HTTP без сохранения состояния), а также может поддерживать такие функции, как двоичные кадры, расширенные протоколы, частично настраиваемые подпротоколы и сжатие.

В настоящее время WebSocket является идеальной заменой для опроса AJAX и Comet. Но некоторые сценарии все же не могут заменить SSE, у WebSocket и SSE есть свои сильные стороны!

3. Рукопожатие WebSocket

Стандарт WebSocket RFC6455 определяет два высокоуровневых компонента: один представляет собой открытое рукопожатие HTTP для согласования параметров соединения, а другой представляет собой механизм кадрирования двоичных сообщений для поддержки передачи текста и двоичных данных на основе сообщений с низкими издержками. Далее мы поговорим об этих двух компонентах высокого уровня.В этой главе подробно рассказывается о деталях рукопожатия, а в следующей главе рассказывается о механизме формирования бинарных сообщений.

Во-первых, напишите такое слово в RFC6455:

Протокол WebSocket пытается реализовать двустороннюю HTTP-связь в существующей HTTP-инфраструктуре и, следовательно, также использует HTTP-порты 80 и 443... Однако эта конструкция не ограничивается связью WebSocket через HTTP, и будущие реализации могут быть реализованы в выделенный На порту используется более простое рукопожатие без переопределения протокола.

--Протокол WebSocket RFC 6455

Из этого отрывка мы можем увидеть «амбиции» человека, разработавшего протокол WebSocket, или насколько далеки планы на будущее.В начале формулировки WebSocket он уже поддерживал рукопожатие на любом порту, а не только полагаясь на HTTP-рукопожатие.

Однако большинство из используемых в настоящее время по-прежнему полагаются на HTTP для рукопожатия. Потому что инфраструктура HTTP уже вполне завершена.

Стандартный процесс рукопожатия

Далее давайте рассмотрим пример конкретного рукопожатия WebSocket. Взять собственный сайт автораthrees.halfrost.com/Например.

Откройте этот веб-сайт, и запрос рукопожатия wss будет открыт, как только веб-страница будет отображена. Запрос рукопожатия выглядит следующим образом:

GET wss://threes.halfrost.com/sockjs/689/8x5nnke6/websocket HTTP/1.1
// 请求的方法必须是GET,HTTP版本必须至少是1.1

Host: threes.halfrost.com
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
// 请求升级到 WebSocket 协议

Origin: https://threes.halfrost.com
Sec-WebSocket-Version: 13
// 客户端使用的 WebSocket 协议版本

User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Mobile Safari/537.36
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: _ga=GA1.2.00000006.14111111496; _gid=GA1.2.23232376.14343448247; Hm_lvt_d60c126319=1524898423,1525574369,1526206975,1526784803; Hm_lpvt_d606319=1526784803; _gat_53806_2=1
Sec-WebSocket-Key: wZgx0uTOgNUsHGpdWc0T+w==
// 自动生成的键,以验证服务器对协议的支持,其值必须是 nonce 组成的随机选择的 16 字节的被 base64 编码后的值

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
// 可选的客户端支持的协议扩展列表,指示了客户端希望使用的协议级别的扩展

По сравнению с обычным протоколом HTTP отличия заключаются в следующем:

Запрошенный URL-адрес начинается с ws:// или wss://, а не с HTTP:// или HTTPS://. Поскольку websocket может использоваться в сценариях, отличных от браузеров, здесь используется настраиваемый URI. Аналогия с HTTP, протокол ws: общий запрос, занимает тот же порт 80, что и HTTP; протокол wss: безопасная передача на основе SSL, занимает тот же порт 443, что и TLS.

Connection: Upgrade
Upgrade: websocket

Эти два места обычно недоступны в обычных HTTP-пакетах.Здесь протокол обновляется с помощью Upgrade, что указывает на то, что выполняется обновление до протокола веб-сокета.

Sec-WebSocket-Version: 13
Sec-WebSocket-Key: wZgx0uTOgNUsHGpdWc0T+w==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

Sec-WebSocket-Version представляет собой версию WebSocket.Первоначально протоколов WebSocket было слишком много, и у разных производителей были свои версии протоколов, но теперь это решено. Если сервер не поддерживает версию, ему необходимо вернуть Sec-WebSocket-Version, которая содержит номер версии, поддерживаемой сервером. (Подробности см. нижеНесколько версий рукопожатия через веб-сокетодин период)

Последняя версия 13. Конечно, могут быть очень ранние версии 7 и 8 (на данный момент версий 7 и 8 в принципе нет)

Уведомление: Хотя черновые версии этого документа (09, 10, 11 и 12) были опубликованы (они представляли собой скорее редакционные правки и уточнения, чем изменения протокола проводки), значения 9, 10, 11 и 12 не Используется как допустимая версия Sec-WebSocket. Эти значения зарезервированы в реестре IANA, но использоваться не будут.

+--------+-----------------------------------------+----------+
|Version |                Reference                |  Status  |
| Number |                                         |          |
+--------+-----------------------------------------+----------+
| 0      + draft-ietf-hybi-thewebsocketprotocol-00 | Interim  |
+--------+-----------------------------------------+----------+
| 1      + draft-ietf-hybi-thewebsocketprotocol-01 | Interim  |
+--------+-----------------------------------------+----------+
| 2      + draft-ietf-hybi-thewebsocketprotocol-02 | Interim  |
+--------+-----------------------------------------+----------+
| 3      + draft-ietf-hybi-thewebsocketprotocol-03 | Interim  |
+--------+-----------------------------------------+----------+
| 4      + draft-ietf-hybi-thewebsocketprotocol-04 | Interim  |
+--------+-----------------------------------------+----------+
| 5      + draft-ietf-hybi-thewebsocketprotocol-05 | Interim  |
+--------+-----------------------------------------+----------+
| 6      + draft-ietf-hybi-thewebsocketprotocol-06 | Interim  |
+--------+-----------------------------------------+----------+
| 7      + draft-ietf-hybi-thewebsocketprotocol-07 | Interim  |
+--------+-----------------------------------------+----------+
| 8      + draft-ietf-hybi-thewebsocketprotocol-08 | Interim  |
+--------+-----------------------------------------+----------+
| 9      +                Reserved                 |          |
+--------+-----------------------------------------+----------+
| 10     +                Reserved                 |          |
+--------+-----------------------------------------+----------+
| 11     +                Reserved                 |          |
+--------+-----------------------------------------+----------+
| 12     +                Reserved                 |          |
+--------+-----------------------------------------+----------+
| 13     +                RFC 6455                 | Standard |
+--------+-----------------------------------------+----------+

[RFC 6455]

The |Sec-WebSocket-Key| header field is used in the WebSocket opening handshake. It is sent from the client to the server to provide part of the information used by the server to prove that it received a valid WebSocket opening handshake. This helps ensure that the server does not accept connections from non-WebSocket clients (e.g., HTTP clients) that are being abused to send data to unsuspecting WebSocket servers.

Поле Sec-WebSocket-Key используется на этапе рукопожатия. Он отправляется от клиента к серверу для предоставления частичного содержимого, которое сервер использует для подтверждения полученной им информации, и эффективно завершает рукопожатие WebSocket. Это помогает гарантировать, что сервер не сможет принимать соединения от клиентов, отличных от WebSocket (например, HTTP-клиентов), которые могут быть использованы для отправки данных на ничего не подозревающий сервер WebSocket.

Sec-WebSocket-Key генерируется браузером случайным образом и обеспечивает базовую защиту от злонамеренных или непреднамеренных подключений.

Sec-WebSocket-Extensions является частью согласования обновления, которое будет подробно объяснено в следующей главе.

Тогда посмотрите на ответ:

HTTP/1.1 101 Switching Protocols
// 101 HTTP 响应码确认升级到 WebSocket 协议
Server: nginx/1.12.1
Date: Sun, 20 May 2018 09:06:28 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: 375guuMrnCICpulKbj7+JGkOhok=
// 签名的键值验证协议支持
Sec-WebSocket-Extensions: permessage-deflate
// 服务器选择的WebSocket 扩展

В ответ ответьте кодом ответа HTTP 101, подтверждающим обновление до протокола WebSocket.

Также есть два заголовка WebSocket:

Sec-WebSocket-Accept: 375guuMrnCICpulKbj7+JGkOhok=
// 签名的键值验证协议支持
Sec-WebSocket-Extensions: permessage-deflate
// 服务器选择的 WebSocket 扩展

Sec-WebSocket-Accept — это зашифрованный ключ Sec-WebSocket-Key после подтверждения сервером.

Метод расчета Sec-WebSocket-Accept выглядит следующим образом:

  1. Сначала извлеките Sec-WebSocket-Key из заголовка запроса клиента и соедините его с 258EAFA5-E914-47DA-95CA-C5AB0DC85B11; (258EAFA5-E914-47DA-95CA-C5AB0DC85B11 этот глобальный уникальный идентификатор (GUID, [RFC4122]) равен единственный адекватный)
  2. Затем выполните хеширование SHA-1, и, наконец, закодированный в base64 результат будет Sec-WebSocket-Accept.

Поддельный код:

> toBase64(sha1( Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ))

Точно так же Sec-WebSocket-Key/Sec-WebSocket-Accept гарантирует только успех рукопожатия во время рукопожатия, но не гарантирует безопасность данных.Использование wss:// будет немного безопаснее.

Подпротоколы в рукопожатии

Рукопожатие WebSocket может включать проблемы с подпротоколом.

Давайте сначала посмотрим на функцию инициализации объекта WebSocket:

WebSocket WebSocket(
in DOMString url, 
// 表示要连接的URL。这个URL应该为响应WebSocket的地址。
in optional DOMString protocols 
// 可以是一个单个的协议名字字符串或者包含多个协议名字字符串的数组。默认设为一个空字符串。
);

Здесь есть необязательный элемент, массив протоколов, по которым можно договориться.

var ws = new WebSocket('wss://example.com/socket', ['appProtocol', 'appProtocol-v2']);

ws.onopen = function () {
if (ws.protocol == 'appProtocol-v2') { 
	...
	} else {
	... 
	}
}

При создании объекта WebSocket вы можете передать необязательный массив подпротоколов, чтобы сообщить серверу, какие протоколы клиент понимает или ожидает, что сервер примет. Сервер может выбрать несколько поддерживаемых протоколов для возврата из данных.Если ни один из них не поддерживается, это напрямую приведет к сбою рукопожатия. Запустите обратный вызов onerror и отключитесь.

Подпротокол здесь может быть настраиваемым протоколом.

Несколько версий рукопожатия через веб-сокет

Используя возможность уведомления о версии WebSocket (поле заголовка Sec-WebSocket-Version), клиент может первоначально запросить версию протокола WebSocket, которую он выбирает (она не обязательно должна быть последней, поддерживаемой клиентом). Если сервер поддерживает запрошенную версию, а в остальном сообщение рукопожатия допустимо, сервер примет эту версию. Если сервер не поддерживает запрошенную версию, он ДОЛЖЕН ответить полем заголовка Sec-WebSocket-Version (или несколькими полями заголовка Sec-WebSocket-Version), содержащим все версии, которые он будет использовать. На этом этапе, если клиент поддерживает уведомленную версию, он может повторить рукопожатие WebSocket с новым значением версии.

Например:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
...
Sec-WebSocket-Version: 25

Сервер не поддерживает версию 25 и вернет:

HTTP/1.1 400 Bad Request
...
Sec-WebSocket-Version: 13, 8, 7

Если клиент поддерживает версию 13, ему необходимо повторить рукопожатие:

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

4. Переговоры об обновлении WebSocket

На фазе рукопожатия Websocket он принесет заголовок Websocket из пяти. Этот пять заголовков и обновлений являются соответствующими консультациями.

  • Sec-WebSocket-Version
    Клиент указывает номер версии (обычно это 13 версия), которую вы хотите использовать, если сервер не поддерживает эту версию, вам необходимо вернуться к их поддерживаемым версиям. После того, как клиент получит ответ, необходимо сообщить номер версии своей собственной поддержке. Клиент должен отправить заголовок.

  • Sec-WebSocket-Key
    Клиент запрашивает автоматически сгенерированный ключ. Этот заголовок ДОЛЖЕН быть отправлен клиентом.

  • Sec-WebSocket-Accept
    Значение ответа, рассчитанное сервером для ключа Sec-WebSocket-Key клиента. Этот заголовочный сервер ДОЛЖЕН отправить.

  • Sec-WebSocket-Protocol
    Используется для согласования подпротоколов приложений: клиент отправляет список поддерживаемых протоколов, а сервер ДОЛЖЕН ответить только одним именем протокола. Если сервер не может поддерживать протокол, прямое рукопожатие не выполняется. Клиент может не отправлять подпротоколы, но после отправки неспособность сервера поддерживать любой из них приведет к сбою рукопожатия. Этот заголовок необязательно отправляется клиентом.

  • Sec-WebSocket-Extensions
    Используется для согласования расширения WebSocket, которое будет использоваться для этого соединения: клиент отправляет поддерживаемое расширение, а сервер подтверждает, что он поддерживает одно или несколько расширений, возвращая тот же заголовок. Этот заголовок необязательно отправляется клиентом. Если сервер не поддерживает его, это не приведет к сбою рукопожатия, но для этого соединения нельзя использовать расширение.

Согласование находится на этапе рукопожатия.После того, как рукопожатие завершено, HTTP-связь заканчивается, и следующий полный дуплекс полностью передается управлению протоколом WebSocket (TCP-связь).

5. Расширение протокола WebSocket

Рабочая группа HyBi, отвечающая за разработку спецификации WebSocket, сделала два расширения, Sec-WebSocket-Extensions:

  • Расширение мультиплексирования для WebSockets
    Это расширение может отделить логическое соединение WebSocket и реализовать совместное использование базового TCP-соединения.

  • Расширения сжатия для WebSocket
    Добавлено сжатие в протокол WebSocket. (например, расширение x-webkit-deflate-frame)

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

Если передача WebSocket осуществляется через HTTP2, производительность будет лучше, ведь HTTP2 изначально поддерживает мультиплексирование потоков. Используя механизм кадрирования HTTP2 для кадрирования веб-сокетов, можно передавать несколько веб-сокетов в одном сеансе.

6. Фрейм данных WebSocket

Еще одним продвинутым компонентом WebSocket является механизм формирования двоичных сообщений. WebSocket разделит сообщение приложения на один или несколько кадров, получатель соберет его после получения нескольких кадров, а затем уведомит получателя после получения полного сообщения.

Структура фрейма данных WebSocket

Формат кадра данных WebSocket следующий:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+
  • FIN: 0 означает не последний осколок, 1 означает, что это последний осколок.

  • РСВ1, РСВ2, РСВ3:

Обычно все 0. Когда клиент и сервер договариваются об использовании расширения WebSocket, эти три флага могут быть 0, а значение значения определяется расширением. Если присутствует ненулевое значение и расширение WebSocket не используется, подключение завершается ошибкой.

  • Опкод:

%x0: указывает кадр продолжения. Когда Opcode равен 0, это означает, что при передаче данных используется фрагментация данных, и текущий полученный кадр данных является одним из фрагментов данных;
%x1: указывает, что это текстовый фрейм;
%x2: указывает, что это двоичный кадр;
%x3-7: зарезервированные коды операций для определенных впоследствии неуправляющих кадров;
%x8: указывает, что соединение разорвано;
% X9: Указывает, что это запрос сердцебиения (ping);
%XA: означает, что это пульс (PONG);
%xB-F: зарезервированные коды операций для определяемых впоследствии управляющих кадров.

  • Маска:

Указывает, следует ли выполнять полезную нагрузку данныхмаскированное исключающее ИЛИработать. 1 означает, что требуется, 0 означает, что не требуется. (Применяется только к сообщениям, отправляемым клиентом на сервер)

  • Лен полезной нагрузки:

Указывает длину полезной нагрузки данных, возможны 3 случая:

Если длина данных составляет от 0 до 125, то len полезной нагрузки достаточно, чтобы быть представленным 7 битами, и представленное число также является длиной полезной нагрузки;
Если длина данных равна 126, то Payload len должна быть представлена ​​7 + 16 битами, а 16-битное целое число без знака, представленное следующими 2 байтами, является длиной этого кадра;
Если длина данных равна 127, то длина полезной нагрузки должна быть представлена ​​7 + 64 битами, а 64-битное целое число без знака, представленное следующими 8 байтами, является длиной этого кадра.

  • Маскирующий ключ:

Если маска = 0, не существует ключ маскировки, если маска = 1, длина клавиши маскировки составляет 4 байта, 32 бита.

Маска представляет собой 32-битное значение, выбранное клиентом случайным образом. При подготовке маскированного кадра клиент ДОЛЖЕН выбрать новый ключ маски из набора разрешенных 32-битных значений. Ключ маски должен быть непредсказуемым, поэтому ключ маски должен исходить из сильного источника энтропии, а ключ маски, используемый для данного кадра, не должен быть легко предсказуем сервером/прокси-сервером для ключа маски, используемого для последующих кадров. Непредсказуемость ключа маски необходима, чтобы авторы вредоносных приложений не могли выбирать байты, которые появляются в сообщении. В RFC 4086 [RFC4086] обсуждается, что требуется для подходящего источника энтропии для приложений, чувствительных к безопасности.

Маска не влияет на длину «данных полезной нагрузки». Для преобразования замаскированных данных в немаскированные или наоборот применяется следующий алгоритм. Один и тот же алгоритм применяется независимо от направления преобразования, например, одни и те же шаги применяются как к маскированным, так и к демаскированным данным.

  • original-octet-i: i-й байт исходных данных.
  • преобразованный октет-i: i-й байт преобразованных данных.
  • j: результат i mod 4.
  • masking-key-octet-j: это j-й байт ключа маски.

Октет i преобразованных данных («преобразованный-октет-i») представляет собой исключающее ИЛИ (XOR) октета i исходных данных («исходный-октет-i») по модулю 4 позиции ключа маски Октет («маскирующий- ключ-октет-j"):

j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j

Алгоритм просто описать: По битовому циклу или вычислению, сначала по индексу этого бита получить соответствующее значение X в Masking-KEY, а затем сделать или сделать или получить реальные данные BYTE.

Примечание. Маска предназначена не для предотвращения утечки данных, а для предотвращения вредоносных скриптов, запущенных в клиенте, от выполнения атак с отравлением кеша прокси на промежуточных устройствах, не поддерживающих WebSockets.

Чтобы понять подробности этой атаки, обратитесь к документу W2SP 2011.Talking to Yourself for Fun and Profit.

Атака в основном делится на 2 этапа,

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

Второй шаг — создать атаку «отравления» на прокси-сервере. Поскольку рукопожатие WebSocket прошло успешно, хакер может отправить данные на свой собственный сервер, отправив тщательно настроенное текстовое сообщение в формате HTTP. Хозяин этих данных должен быть подделан как сервер, к которому собирается получить доступ обычный пользователь, а запрошенный ресурс — это ресурс, который обычный пользователь собирается запросить. Прокси-сервер будет думать, что это новый запрос, поэтому он запрашивает собственный сервер хакера. В это время собственный сервер хакера также должен сотрудничать. После получения этого «отравленного» сообщения он немедленно вернет «яд» и вернет некоторые ресурсы вредоносных скриптов и т. д. Пока что «отравление» прошло успешно.

Когда пользователь запрашивает безопасный ресурс через прокси-сервер, поскольку хост и URL-адрес кэшируются на прокси-сервере хакерами с использованием текстовой информации в формате HTTP, «отравленные» ресурсы также кэшируются. В это время пользователь запрашивает то же самое. При обнаружении ресурсов хоста и URL-адреса прокси-кэш-сервер обнаруживает, что они были закэшированы, и сразу же возвращает вредоносный скрипт или ресурс после «отравления» пользователю. В этот момент пользователь подвергается нападению.

  • Payload Data:

Данные полезной нагрузки делятся на два типа: данные расширения и данные приложения.

Расширенные данные: если расширение не согласовано, расширенные данные составляют 0 байт. Длина расширенных данных, если они есть, должна быть зафиксирована на этапе установления связи. Длина данных полезной нагрузки также включает данные расширения.

Данные приложения: если есть расширенные данные, они сортируются после расширенных данных.

Фрейм управления WebSocket

Кадр управления определяется кодом операции, старший бит кода операции равен 1. В настоящее время определенные коды операций для управляющих кадров включают 0x8 (закрытие), 0x9 (Ping) и 0xA (Pong). Коды операций 0xB-0xF зарезервированы для будущих управляющих кадров, которые еще не определены.

Кадры управления используются для передачи статуса веб-сокетов. Кадры управления могут быть вставлены в середину сообщений с фреймами.

Все кадры управления должны иметь длину полезной нагрузки не более 125 байтов, а кадры управления не должны быть кадрированы.

  • После получения кадра управления с кодом операции 0x8 Close базовое TCP-соединение может быть закрыто. Клиент также может дождаться завершения работы сервера и отсутствия ответа в течение определенного периода времени, а затем закрыть собственное TCP-соединение.

Предлагаемый код состояния при закрытии указан в RFC 6455. Нормативного определения нет, но дается предопределенный код состояния.

код состояния инструкция Зарезервировано ✔︎ или недоступно ✖︎
0-999 Коды состояния в этом диапазоне не используются. ✖︎
1000 Указывает на корректное завершение работы, означающее, что предложенное соединение завершено.
1001 Указывает, что конечная точка «уходит», например, при завершении работы сервера или переходе браузера на другую страницу.
1002 Указывает, что конечная точка разорвала соединение из-за ошибки протокола.
1003 Указывает, что конечная точка разорвала соединение, так как получила тип данных, который она не может принять (например, конечная точка понимает только текстовые данные, но получила двоичное сообщение).
1004 Резерв.可能在将来定义其具体的含义。 ✔︎
1005 является зарезервированным значением и НЕ ДОЛЖНО устанавливаться конечной точкой в ​​кадре управления завершением работы. Он предназначен для использования в приложениях, ожидающих, что код состояния укажет, что на самом деле код состояния отсутствует. ✔︎
1006 является зарезервированным значением и НЕ ДОЛЖНО устанавливаться конечной точкой в ​​кадре управления завершением работы. Он предназначен для использования в приложениях, которые ожидают код состояния, указывающий, что соединение было закрыто ненормально. ✔︎
1007 Указывает, что конечная точка разорвала соединение, поскольку данные, полученные в сообщении, не соответствовали типу сообщения (например, данные, отличные от UTF-8 [RFC3629], в текстовом сообщении).
1008 Указывает на то, что конечная точка, потому что полученные сообщения нарушают свою политику и прекращают соединение. Это состояние, когда нет другого подходящего кода (например, 1003 или 1009) или может быть универсальным кодом состояния, возвращается, если необходимо скрыть детали стратегии.
1009 Указывает, что конечная точка слишком велика, чтобы завершить ее из-за полученного сообщения.
1010 Указывает, что конечная точка (клиент) разорвала соединение, так как ожидала, что сервер согласует одно или несколько расширений, но сервер не вернул их в ответном сообщении рукопожатия WebSocket. Список необходимых расширений должен появиться в разделе причин заключительного фрейма.
1011 Указывает, что сервер разорвал соединение, так как столкнулся с непредвиденным обстоятельством, препятствующим выполнению запроса.
1012
1013
1014
1015 является зарезервированным значением и не может быть установлено конечной точкой в ​​качестве кода состояния в закрытом кадре. Он предназначен для использования в приложениях, которые ожидают код состояния, указывающий, что соединение было закрыто из-за сбоя подтверждения TLS (например, не удалось проверить сертификат сервера). ✔︎
1000-2999 Коды состояния в этой области зарезервированы для определения расширений, указанных в настоящем Соглашении, его будущих редакциях, а также постоянной и существующей общедоступной спецификации. ✔︎
3000-3999 Коды состояния в диапазоне зарезервированы для библиотек, каркасов и приложений. Эти коды состояния зарегистрированы непосредственно к IANA. Настоящая спецификация, чтобы объяснить эти неопределенные коды состояния. ✔︎
4000-4999 Коды состояния в этой области зарезервированы для частного использования и поэтому не могут быть зарегистрированы. Эти коды состояния могут использоваться предыдущими протоколами между приложениями WebSocket. Эта спецификация не определяет интерпретацию этих кодов состояния. ✔︎
  • Когда получен управляющий кадр с кодом операции 0x9 Ping, ответ на кадр, содержащий код операции pong, должен быть отправлен немедленно, если только не получен закрытый кадр. Оба конца будут отправлять кадры Ping в любое время после установления соединения и до его закрытия. Кадры Ping могут содержать «данные приложения». Кадры Ping можно использовать в качестве пакетов подтверждения активности.

  • Когда получен контрольный кадр кода операции 0xA pong, известно, что другая сторона все еще может ответить. Кадры Pong должны содержать те же данные, что и данные приложения отвечающего кадра Ping. Если терминал принимает кадр Ping и не отправил ответ Pong на предыдущий кадр Ping, терминал может выбрать отправку кадра Pong на самый последний обработанный кадр Ping. Кадр Pong может быть отправлен незапрошенным, что действует как одностороннее сердцебиение. Старайтесь не активно отправлять кадры понга.

Правила формирования веб-сокетов

Правила обрамления определяются RFC6455, а приложение не знает о том, как кадра. Обрамление выполняется клиентом и сервером.

При кадрировании также лучше используется расширение протокола мультиплексирования, которое требует возможности разбивать сообщения на более мелкие сегменты для более эффективного совместного использования выходных каналов.

Правила кадрирования, указанные в RFC 6455, следующие:

  • Нефрагментированное сообщение состоит из одного кадра с установленным битом FIN и ненулевым кодом операции.
  • Фрагментированное сообщение состоит из одного кадра со сброшенным битом FIN и ненулевым кодом операции, за которым следует ноль или более кадров со сброшенным битом FIN и кодом операции, установленным на 0, и заканчивается кадром со сброшенным битом FIN и кодом операции. код операции установлен на 0. Фрейм с установленным битом FIN и кодом операции 0. Фрагментированное сообщение концептуально эквивалентно одному большому сообщению, полезная нагрузка которого эквивалентна объединению фрагментов в последовательности; однако при наличии расширений это может не относиться к существованию «расширенных данных», определенных объяснением расширения . Например, «данные расширения» могут существовать только в начале первого сегмента и применяться к последующим сегментам, или «данные расширения» могут существовать только для каждого сегмента до определенного сегмента. В следующем примере показано, как работает сегментация без «расширенных данных».

Пример: для текстового сообщения, отправленного в виде трех сегментов, первый сегмент будет иметь код операции 0x1 и очищенный бит FIN, второй сегмент будет иметь код операции 0x0 и очищенный бит FIN, а третий сегмент будет иметь код операции 0x0 и бит FIN. Бит FIN очищен. Фрагмент будет иметь код операции 0x0 и установленный бит FIN. (Код операции 0x0 был объяснен выше, указывая на кадр продолжения. Когда код операции O равен 0x0, это означает, что эта передача данных использует фрагментацию данных, и текущий полученный кадр данных является одним из фрагментов данных;)

  • Кадры управления могут быть введены в середину фрагментированного сообщения. Сам кадр управления не должен быть сегментирован.
  • Фрагмент сообщения должен быть доставлен получателю в порядке отправителя.
  • Сообщение во фрагменте НЕ ДОЛЖНО чередоваться с другим сообщением во фрагменте, если только не было согласовано расширение, учитывающее чередование.
  • Конечная точка ДОЛЖНА иметь возможность обрабатывать кадры управления в середине фрагментированного сообщения.
  • Отправитель может создавать фрагменты любого размера для неуправляемых сообщений.
  • Клиенты и серверы должны поддерживать получение фрагментированных и нефрагментированных сообщений.
  • Поскольку кадры управления не могут быть фрагментированы, ПО промежуточного слоя НЕ ДОЛЖНО пытаться изменить фрагментацию кадров управления.
  • Промежуточное программное обеспечение не должно изменять фрагментацию сообщения, если используются какие-либо зарезервированные биты, и значение этих значений неизвестно промежуточным программным обеспечением.
  • В контексте соединения, где расширения были согласованы, а промежуточное ПО не знает о семантике согласованных расширений, промежуточное ПО НЕ ДОЛЖНО изменять фрагментацию любых сообщений. Точно так же промежуточное ПО, которое не видит рукопожатие WebSocket (и не уведомляется о его содержании), приводящее к установлению соединения WebSocket, НЕ ДОЛЖНО изменять фрагментацию любых сообщений для ссылки.
  • В соответствии с этими правилами все фрагменты сообщения имеют один и тот же тип, заданный кодом операции первого фрагмента. Поскольку кадры управления не могут быть фрагментированы, тип, используемый для всех фрагментов в сообщении, должен быть либо текстовым, либо двоичным, либо зарезервированным кодом операции. Примечание. Если кадры управления не могут быть вставлены, задержка проверки связи, например, если за ней следует большое сообщение, будет очень длительной. Следовательно, необходимо обрабатывать кадры управления в середине фрагментированных сообщений.

Примечание по реализации: при отсутствии каких-либо расширений приемнику не нужно буферизовать весь кадр для его обработки. Например, если используется потоковый API, часть кадра может быть доставлена ​​приложению. Однако обратите внимание, что это предположение может не выполняться для всех будущих расширений WebSocket.

7. API WebSocket и формат данных

1. WebSocket API

API WebSocket очень лаконичен, а функции, которые можно вызывать, следующие:

var ws = new WebSocket('wss://example.com/socket');
ws.onerror = function (error) { ... }
ws.onclose = function () { ... }
ws.onopen = function () {
ws.send("Connection established. Hello server!");
}
ws.onmessage = function(msg) {
	if(msg.data instanceof Blob) {
   		processBlob(msg.data);
  	} else {
       processText(msg.data);
   }
}

За исключением нового объекта WebSocket и метода send(), осталось 4 метода обратного вызова.

Среди вышеперечисленных методов особого внимания заслуживает метод send(), этот метод является асинхронным, а не синхронным методом. Это означает, что когда мы бросаем содержимое для отправки в эту функцию, функция возвращается асинхронно, в это времяНе путайте это с отправленным. Сам WebSocket имеет механизм очередей, данные сначала будут сброшены в буфер данных, а затем отправлены в порядке очереди.

Если это огромная файловая очередь, некоторые сообщения с более высоким приоритетом, чем это сообщение, приходят позже, например, системная ошибка, и соединение необходимо немедленно разорвать. Поскольку большой файл ставится в очередь после большого файла, он должен дождаться отправки большого файла, прежде чем отправлять это сообщение с более высоким приоритетом. Это создает проблему блокировки в начале очереди, что приводит к задержке сообщений с более высоким приоритетом.

Разработчики API WebSocket учли это и предоставили нам еще два других свойства, которые могут изменить поведение объектов WebSocket: одно — bufferedAmount, а другое — binaryType.

if (ws.bufferedAmount == 0)
    ws.send(evt.data);

В приведенном выше случае вы можете использовать bufferedAmount для контроля количества буферов, чтобы избежать проблемы блокировки в начале очереди, Его также можно комбинировать с Priority Queue для отправки сообщений в соответствии с приоритетом.

2. Формат данных

WebSocket не имеет ограничений по формату передачи, он может быть текстовым или бинарным. В протоколе поле типа кода операции используется, чтобы различать, является ли он UTF-8 или двоичным. API WebSocket может получать объекты DOMString в кодировке UTF-8, а также двоичные данные, такие как ArrayBuffer, ArrayBufferView или Blob.

Если браузер не устанавливает вручную какие-либо другие параметры для полученных данных, обработка по умолчанию заключается в том, что текст по умолчанию преобразуется в объект DOMString, а двоичные данные или объект Blob будут напрямую переданы в приложение без какой-либо промежуточной обработки. .

var ws = new WebSocket('wss://example.com/socket'); 
ws.binaryType = "arraybuffer";

Единственное место, где можно вмешаться, — это привести все полученные бинарные данные к типу массива буферов, а не к типу BLOB-объектов. Что касается того, зачем преобразовывать в тип буфера массива, кандидаты W3C предложили следующее:

Пользовательские агенты могут использовать этот параметр как подсказку, чтобы решить, что делать с полученными двоичными данными: если для этого параметра установлено значение «BLOB», он может безопасно сбросить его на диск; если для него установлено значение «arraybuffer», это, вероятно, более эффективно. чтобы обрабатывать его в памяти. Естественно, мы поощряем пользовательские агенты использовать более тонкие подсказки, чтобы решить, следует ли помещать входящие данные в память.

—— Рекомендация кандидата W3C API WebSocket

Проще говоря: если вы преобразовали объект BLOB, представляет собой неизменный объект файла или исходные данные. Если нет модификации или не нарезать его, чтобы сохранить объект BLOB - хороший выбор. Если вы хотите иметь дело с этими необработанными данными в память, что, очевидно, будет более подходящим лечением, тогда, пожалуйста, передавайте в тип ArrayBuffer.

8. Производительность WebSocket и сценарии использования

Вот тест с сайта WebSocket.org, сравнивающий опрос XHR с WebSocket:

На приведенном выше рисунке мы сначала смотрим на синюю гистограмму, которая представляет собой трафик, потребляемый опросом.В этом тесте служебная информация заголовка HTTP-запроса и ответа включает в себя в общей сложности 871 байт. Конечно, каждый раз, когда вы тестируете другой запрос, накладные расходы заголовка различны. Этот тест полностью протестирован с запросом 871 байт.

Use case A: 1,000 clients polling every second: Network throughput is (871 x 1,000) = 871,000 bytes = 6,968,000 bits per second (6.6 Mbps)
Use case B: 10,000 clients polling every second: Network throughput is (871 x 10,000) = 8,710,000 bytes = 69,680,000 bits per second (66 Mbps)
Use case C: 100,000 clients polling every 1 second: Network throughput is (871 x 100,000) = 87,100,000 bytes = 696,800,000 bits per second (665 Mbps)
Фрейм Websocket — это всего два байта служебных данных вместо 871, что заменяет 871 байт опроса всего на 2 байта!

Use case A: 1,000 clients receive 1 message per second: Network throughput is (2 x 1,000) = 2,000 bytes = 16,000 bits per second (0.015 Mbps)
Use case B: 10,000 clients receive 1 message per second: Network throughput is (2 x 10,000) = 20,000 bytes = 160,000 bits per second (0.153 Mbps)
Use case C: 100,000 clients receive 1 message per second: Network throughput is (2 x 100,000) = 200,000 bytes = 1,600,000 bits per second (1.526 Mbps)

Для того же количества клиентских опросов в секунду, когда количество раз достигает 10 Вт/с, опросу опроса необходимо потреблять 665 Мбит/с, в то время как Websocket потребляет только 1,526 Мбит/с, что составляет почти 435 раз! !

Судя по результатам, WebSocket действительно намного лучше, чем эффективность опроса и потребление скорости сети.

С точки зрения сценариев использования XHR, SSE и WebSocket имеют свои преимущества и недостатки.

XHR проще, чем два других метода, и его легко реализовать, полагаясь на совершенную инфраструктуру HTTP. Однако он не поддерживает потоки запросов и не имеет идеальной поддержки соответствующих потоков (требуется поддержка Streams API для поддержки потоков ответов). Что касается формата передаваемых данных, то поддерживается как текстовый, так и двоичный формат, а также поддерживается сжатие. HTTP отвечает за кадрирование своих пакетов.

SSE также не поддерживает потоки запросов.После рукопожатия сервер может отправлять данные в виде потока ответа клиенту, используя протокол источника события. SSE поддерживает только текстовые данные, а не двоичные. Поскольку SSE не предназначен для передачи двоичного кода, при необходимости двоичные объекты можно закодировать в формате base64, а затем передать с помощью SSE. SSE также поддерживает сжатие, а за его кадрирование отвечает поток событий.

WebSocket в настоящее время является единственным полнодуплексным протоколом, реализованным через одно и то же соединение TCP, и потоки запросов и ответов прекрасно поддерживаются. Поддерживает текстовые и двоичные данные и имеет собственное двоичное кадрирование. С точки зрения сжатия чуть хуже, т.к. некоторые не поддерживаются, например расширение x-webkit-deflate-frame, сервер не поддерживает сжатие в ws запросе в дистанции выше.

Было бы лучше, если бы все сетевые среды могли поддерживать WebSocket или SSE. Но это нереально, сетевая среда постоянно меняется, некоторые сети могут блокировать связь через WebSocket, или пользовательское оборудование не поддерживает протокол WebSocket, поэтому XHR играет роль.

Если клиенту не нужно отправлять сообщения на сервер, а нужны только постоянные обновления в реальном времени, то также неплохо рассмотреть возможность использования SSE. Однако в настоящее время SSE плохо поддерживается в IE и Edge. В этом отношении WebSocket сильнее SSE.

Следовательно, разные протоколы следует выбирать в соответствии с разными сценариями, и каждый из них имеет свои сильные стороны.


Ссылка:

RFC6455
Руководство по отправлению серверов
Comet: технология «Server Push», основанная на постоянном соединении HTTP
Полное руководство по веб-производительности
What is Sec-WebSocket-Key for?
10.3. Attacks On Infrastructure (Masking)
Why are WebSockets masked?
How does websocket frame masking protect against cache poisoning?
What is the mask in a WebSocket frame?

Репозиторий GitHub:Halfrost-Field

Follow: halfrost · GitHub

Source: GitHub.com/HAL мороз/га…