Подключение к веб сокетам
Веб-сокеты - технология, которая позволяет открыть интерактивную сессию общения между браузером пользователя и сервером. Соединяясь через веб-сокеты, веб-приложения могут осуществлять взаимодействие в реальном времени вместо того, чтобы делать запросы к клиенту о входящих/исходящих изменениях.
Замечание: У нас есть работающий пример чата, части кода из которого используются в статье. Пример будет доступен, когда инфраструктура сайта сможет должным образом поддерживать хостинг примеров с использованием веб-сокетов.
Формат данных
Полное описание протокола содержится в RFC 6455.
Здесь представлено частичное описание с комментариями самых важных его частей. Если вы хотите понять стандарт, то рекомендуется сначала прочитать это описание.
Закрытие соединения
Когда вы закончили использовать соединение WebSocket, закройте его используя метод close() :
Перед попыткой закрыть соединение может быть полезно проверить атрибут bufferedAmount чтобы определить, не переданы ли еще какие-либо данные по сети.
Передача данных
Поток данных в WebSocket состоит из «фреймов», фрагментов данных, которые могут быть отправлены любой стороной, и которые могут быть следующих видов:
- «текстовые фреймы» – содержат текстовые данные, которые стороны отправляют друг другу.
- «бинарные фреймы» – содержат бинарные данные, которые стороны отправляют друг другу.
- «пинг-понг фреймы» используется для проверки соединения; отправляется с сервера, браузер реагирует на них автоматически.
- также есть «фрейм закрытия соединения» и некоторые другие служебные фреймы.
В браузере мы напрямую работаем только с текстовыми и бинарными фреймами.
Метод WebSocket .send() может отправлять и текстовые и бинарные данные.
Вызов socket.send(body) принимает body в виде строки или любом бинарном формате включая Blob , ArrayBuffer и другие. Дополнительных настроек не требуется, просто отправляем в любом формате.
При получении данных, текст всегда поступает в виде строки. А для бинарных данных мы можем выбрать один из двух форматов: Blob или ArrayBuffer .
Это задаётся свойством socket.binaryType , по умолчанию оно равно "blob" , так что бинарные данные поступают в виде Blob -объектов.
Формат текстовых данных
Текст, полученный через WebSocket должен иметь кодировку UTF-8
До Gecko 9.0 (Firefox 9.0 / Thunderbird 9.0 / SeaMonkey 2.6), некоторые не символьные значения в допустимом тексте UTF-8 могут привести к разрыву соединения. Теперь Gecko допускает эти значения.
Использование протокола WebSocket в Node.js
Протокол WebSocket поддерживают все современные браузеры.
Состояние соединения
Чтобы получить состояние соединения, существует дополнительное свойство socket.readyState со значениями:
- 0 – «CONNECTING»: соединение ещё не установлено,
- 1 – «OPEN»: обмен данными,
- 2 – «CLOSING»: соединение закрывается,
- 3 – «CLOSED»: соединение закрыто.
Ошибки подключения
Если ошибка случается во время попытки подключения, то в объект WebSocket сначала посылается простое событие с именем «error» (таким образом, задействуя обработчик onerror ), потом - событие CloseEvent (таким образом, задействуя обработчик onclose ) чтобы обозначить причину закрытия соединения.
▍Выполнение PUT-запросов и DELETE-запросов
Выполнение таких запросов выглядит так же, как и выполнение POST-запросов. Главное отличие, помимо смыслового наполнения таких операций, заключается в значении свойства method объекта options .
Эта библиотека основана на промисах, она обладает некоторыми преимуществами перед стандартными механизмами, в частности, перед API Fetch. Среди её преимуществ можно отметить следующие:
- Поддержка старых браузеров (для использования Fetch нужен полифилл).
- Возможность прерывания запросов.
- Поддержка установки тайм-аутов для запросов.
- Встроенная защита от CSRF-атак.
- Поддержка выгрузки данных с предоставлением сведений о ходе этого процесса.
- Поддержка преобразования JSON-данных.
- Работа в Node.js
Установка
Для установки Axios можно воспользоваться npm:
Того же эффекта можно достичь и при работе с yarn:
API Axios
Но обычно удобнее пользоваться специальными методами:
- axios.delete()
- axios.put()
- axios.patch()
- axios.options()
Запросы GET
Axios удобно использовать с применением современного синтаксиса async/await. В следующем примере кода, рассчитанном на Node.js, библиотека используется для загрузки списка пород собак из API Dog. Здесь применяется метод axios.get() и осуществляется подсчёт пород:
То же самое можно переписать и без использования async/await, применив промисы:
Использование параметров в GET-запросах
GET-запрос может содержать параметры, которые в URL выглядят так:
При использовании Axios запрос подобного рода можно выполнить так:
Того же эффекта можно достичь, настроив свойство params в объекте с параметрами:
Запросы POST
Выполнение POST-запросов очень похоже на выполнение GET-запросов, но тут, вместо метода axios.get() , используется метод axios.post() :
В качестве второго аргумента метод post принимает объект с параметрами запроса:
▍Защищённая версия протокола WebSocket
Безопасность
WebSocket особенно хорош для сервисов, которые нуждаются в постоянном обмене данными, например онлайн игры, торговые площадки, работающие в реальном времени, и т.д.
Описание фрейма
В протоколе WebSocket предусмотрены несколько видов пакетов («фреймов»).
Они делятся на два больших типа: фреймы с данными («data frames») и управляющие («control frames»), предназначенные для проверки связи (PING) и закрытия соединения.
Фрейм, согласно стандарту, выглядит так:
С виду – не очень понятно, во всяком случае, для большинства людей.
Позвольте пояснить: читать следует слева-направо, сверху-вниз, каждая горизонтальная полоска это 32 бита.
То есть, вот первые 32 бита:
Сначала идёт бит FIN (вертикальная надпись на рисунке), затем биты RSV1, RSV2, RSV3 (их смысл раскрыт ниже), затем «опкод», «МАСКА» и, наконец, «Длина тела», которая занимает 7 бит. Затем, если «Длина тела» равна 126 или 127, идёт «Расширенная длина тела», потом (на следующей строке, то есть после первых 32 бит) будет её продолжение, ключ маски, и потом данные.
У всех фреймов, кроме последнего, этот фрагмент установлен в 0 , у последнего – в 1 .
RSV1, RSV2, RSV3: 1 бит каждый
В обычном WebSocket равны 0 , предназначены для расширений протокола. Расширение может записать в эти биты свои значения.
Задаёт тип фрейма, который позволяет интерпретировать находящиеся в нём данные. Возможные значения:
Если этот бит установлен, то данные фрейма маскированы. Более подробно маску и маскирование мы рассмотрим далее.
Длина тела: 7 битов, 7+16 битов, или 7+64 битов
Если значение поле «Длина тела» лежит в интервале 0-125 , то оно обозначает длину тела (используется далее). Если 126 , то следующие 2 байта интерпретируются как 16-битное беззнаковое целое число, содержащее длину тела. Если 127 , то следующие 8 байт интерпретируются как 64-битное беззнаковое целое, содержащее длину.
Ключ маски: 4 байта.
Если бит Маска установлен в 0, то этого поля нет. Если в 1 то эти байты содержат маску, которая налагается на тело (см. далее).
Данные фрейма (тело)
Состоит из «данных расширений» и «данных приложения», которые идут за ними. Данные расширений определяются конкретными расширениями протокола и по умолчанию отсутствуют. Длина тела должна быть равна указанной в заголовке.
▍Ответ
После того, как сервер получает отправленный клиентом запрос, он его обрабатывает и отправляем клиенту ответ.
Если что-то пошло не так, тут могут быть и другие коды. Например, следующие:
- 404 Not Found
- 403 Forbidden
- 301 Moved Permanently
- 500 Internal Server Error
- 304 Not Modified
- 401 Unauthorized
Разбор HTML-кода
После того, как браузер получает ответ сервера, в теле которого содержится HTML-код, он начинает его разбирать, повторяя вышеописанный процесс для каждого ресурса, который нужен для формирования страницы. К таким ресурсам относятся, например, следующие:
- CSS-файлы.
- Изображения.
- Значок веб-страницы (favicon).
- JavaScript-файлы.
Расширения и подпротоколы
Могут быть дополнительные заголовки Sec-WebSocket-Extensions и Sec-WebSocket-Protocol , описывающие расширения и подпротоколы.
Sec-WebSocket-Extensions: deflate-frame означает, что браузер поддерживает сжатие данных. Расширение – это что-то, связанное с передачей данных, расширяющее сам протокол WebSocket. Заголовок Sec-WebSocket-Extensions отправляется браузером автоматически со списком всевозможных расширений, которые он поддерживает.
Этот необязательный заголовок ставим мы сами, передавая массив подпротоколов вторым параметром new WebSocket , вот так:
Сервер должен ответить перечнем протоколов и расширений, которые он может использовать.
Здесь сервер отвечает, что поддерживает расширение – deflate-frame и может использовать только протокол SOAP из всего списка запрошенных подпротоколов.
▍Выполнение GET-запросов
Вот пример выполнения GET-запроса средствами Node.js:
Доступность веб-сокетов
API веб-сокетов доступно в Javascript коде, область видимости которого включает объект DOM Window или любой объект, реализующий WorkerUtils ; это означает, что вы можете использовать Web Workers.
Замечание: API веб-сокетов (как и протокол лежащий в его основе) всё ещё проходят этап активной разработки; в настоящее время существует много проблем совместимости с разными браузерами (и даже с разными релизами одного и того же браузера).
▍Создание WebSocket-соединения
Для создания WebSocket-соединения нужно воспользоваться соответствующим конструктором:
После успешного установления соединения вызывается событие open . Организовать прослушивание этого события можно, назначив функцию обратного вызова свойству onopen объекта connection :
Для обработки ошибок используется обработчик события onerror :
Коды закрытия
Установление WebSocket-соединения
Протокол WebSocket работает над TCP.
Ограничение скорости
Представим, что наше приложение генерирует много данных для отправки. Но у пользователя медленное соединение, возможно, он в интернете с мобильного телефона и не из города.
Мы можем вызывать socket.send(data) снова и снова. Но данные будут буферизованы (сохранены) в памяти и отправлены лишь с той скоростью, которую позволяет сеть.
Свойство socket.bufferedAmount хранит количество байт буферизованных данных на текущий момент, ожидающих отправки по сети.
Мы можем изучить его, чтобы увидеть, действительно ли сокет доступен для передачи.
Установление соединения
Пример запроса от браузера при создании нового объекта new WebSocket("ws://server.example.com/chat") :
Все заголовки, кроме GET и Host , браузер генерирует сам, без возможности вмешательства JavaScript.
Сервер может проанализировать эти заголовки и решить, разрешает ли он WebSocket с данного домена Origin .
Ответ сервера, если он понимает и разрешает WebSocket -подключение:
Здесь строка Sec-WebSocket-Accept представляет собой перекодированный по специальному алгоритму ключ Sec-WebSocket-Key . Браузер использует её для проверки, что ответ предназначается именно ему.
Итого
WebSocket – это современный способ иметь постоянное соединение между браузером и сервером.
- Нет ограничений, связанных с кросс-доменными запросами.
- Имеют хорошую поддержку браузерами.
- Могут отправлять/получать как строки, так и бинарные данные.
- socket.send(data) ,
- socket.close([code], [reason]) .
WebSocket сам по себе не содержит такие функции, как переподключение при обрыве соединения, аутентификацию пользователей и другие механизмы высокого уровня. Для этого есть клиентские и серверные библиотеки, а также можно реализовать это вручную.
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Протокол WebSocket (стандарт RFC 6455) предназначен для решения любых задач и снятия ограничений обмена данными между браузером и сервером.
Он позволяет пересылать любые данные, на любой домен, безопасно и почти без лишнего сетевого трафика.
▍Отправка запроса
Запрос представляет собой структурированный в соответствии с правилами используемого протокола фрагмент текста. Он состоит из трёх частей:
- Строка запроса.
- Заголовок запроса.
- Тело запроса.
Строка запроса
Строка запроса представляет собой одну текстовую строку, в которой содержатся следующие сведения:
Заголовок запроса
Заголовок запроса представлен набором пар вида поле: значение . Существуют 2 обязательных поля заголовка, одно из которых — Host , а второе — Connection . Остальные поля необязательны.
Заголовок может выглядеть так:
Поле Host указывает на доменное имя, которое интересует браузер. Поле Connection , установленное в значение close , означает, что соединение между клиентом и сервером не нужно держать открытым.
Среди других часто используемых заголовков запросов можно отметить следующие:
- Origin
- Accept
- Accept-Encoding
- Cookie
- Cache-Control
- Dnt
Заголовок запроса завершается пустой строкой.
Тело запроса
Тело запроса необязательно, в GET-запросах оно не используется. Тело запроса используется в POST-запросах, а также в других запросах. Оно может содержать, например, данные в формате JSON.
Так как сейчас речь идёт о GET-запросе, тело запроса будет пустым, с ним мы работать не будем.
Закрытие подключения
Обычно, когда сторона хочет закрыть соединение (браузер и сервер имеют равные права), они отправляют «фрейм закрытия соединения» с кодом закрытия и указывают причину в виде текста.
Метод для этого:
- code – специальный WebSocket-код закрытия (не обязателен).
- reason – строка с описанием причины закрытия (не обязательна).
Затем противоположная сторона в обработчике события close получит и код code и причину reason , например:
code – это не любое число, а специальный код закрытия WebSocket.
Наиболее распространённые значения:
- 1000 – по умолчанию, нормальное закрытие,
- 1006 – невозможно установить такой код вручную, указывает, что соединение было потеряно (нет фрейма закрытия).
Есть и другие коды:
Полный список находится в RFC6455, §7.4.1.
Маска для защиты от атаки
Для того, чтобы защититься от атаки, и придумана маска.
Маска служит двум целям:
Наложение маски требует дополнительных ресурсов, поэтому протокол WebSocket не требует её.
Если по этому протоколу связываются два клиента (не обязательно браузеры), доверяющие друг другу и посредникам, то можно поставить бит Маска в 0 , и тогда ключ маски не указывается.
Одним из главных преимуществ технологии — это ее простота. На клиенте и сервере есть всего 4 события для обработки:
Почему Websocket?
Кроме ws существуют еще два способа непрерывной передачи данных: Server-Sent Events (SSE) и Long Polling.
Приведем сравнения механизмов непрерывной связи сервера и клиента, а также сделаем выводы, почему стоит (или не стоит) использовать вебсокет.
- Делаем запрос на изменения;
- Если изменения на сервере появились, то сервер их отправляет;
- Когда клиент получает изменения, клиент делает новый запрос.
Пример работы простейшего api.
Что здесь происходит?
Функция “on” помогает управлять событиями websocket. Самым примечательным является событие message, так что рассмотрим его подробнее.
Не стоит использовать такой подход в реальном приложении. Если описать api таким образом, то сервер не может отличить один запрос от другого. О том, как можно построить api на основе websocket будет написано далее.
Взаимодействие с сервером на клиенте будет выглядеть так:
API на основе Websocket
В отличие от REST API, где запросы распределены по разным url, Websocket API имеет только один url. Для того, чтобы построить полноценное API на основе вебсокетов, необходимо научить систему отличать один запрос от другого. Это можно реализовать следующим образом:
1) С клиента мы будем передавать запросы в виде строки-json, которую распарсим на сервере:
2) На сервере мы распарсим строку и выделем в ней поле event — тип запроса. Пропишем для каждого типа соответствующий ответ:
Таким образом мы можем отправлять разные запросы на сервер и обрабатывать ответ в зависимости от запроса.
Заключение
Если вам была дана задача сделать API и вы узнали, что поддержка старых браузеров заказчика не интересует, то API на основе WebSocket — отличный выбор. Для вашего удобства мы подготовили код клиентской и серверной части по ссылке.
Если вы когда-нибудь проходили собеседование в IT-сфере, то вас могли спросить о том, что происходит, когда вы вводите нечто в адресную строку браузера и нажимаете Enter. Пожалуй, это один из самых популярных вопросов, который встречается на подобных собеседованиях. Тот, кто задаёт подобные вопросы, хочет узнать, можете ли вы объяснить некоторые довольно-таки простые концепции и выяснить, понимаете ли вы принципы работы интернета.
Этот вопрос затрагивает множество технологий, понимать общие принципы которых — значит понимать, как устроена одна из самых сложных систем из когда-либо построенных человечеством, которая охватывает весь мир.
Фрагментация
Например, идёт поиск в базе данных и что-то уже найдено, а что-то ещё может быть позже.
Атака «отравленный кэш»
В ранних реализациях WebSocket существовала уязвимость, называемая «отравленный кэш» (cache poisoning).
Она позволяла атаковать кэширующие прокси-сервера, в частности, корпоративные.
Атака осуществлялась так:
Хакер заманивает доверчивого посетителя (далее Жертва) на свою страницу.
Прокси послушно проглотит этот ответ и закэширует «якобы jQuery».
Поэтому эта атака и называется «отравленный кэш».
Такая атака возможна не для любых прокси, но при анализе уязвимости было показано, что она не теоретическая, и уязвимые прокси действительно есть.
Поэтому придумали способ защиты – «маску».
Пример чата
От JavaScript мы хотим 3 вещи:
Серверный код выходит за рамки этой главы. Здесь мы будем использовать Node.js, но вы не обязаны это делать. Другие платформы также поддерживают средства для работы с WebSocket.
Серверный алгоритм действий будет таким:
Вот рабочий пример:
Вы также можете скачать его (верхняя правая кнопка в ифрейме) и запустить локально. Только не забудьте установить Node.js и выполнить команду npm install ws до запуска.
Расширения и подпротоколы
Также возможны дополнительные заголовки Sec-WebSocket-Extensions и Sec-WebSocket-Protocol , описывающие расширения и подпротоколы (subprotocol), которые поддерживает данный клиент.
Посмотрим разницу между ними на двух примерах:
Заголовок Sec-WebSocket-Extensions: deflate-frame означает, что браузер поддерживает модификацию протокола, обеспечивающую сжатие данных.
Это говорит не о самих данных, а об улучшении способа их передачи. Браузер сам формирует этот заголовок.
Заголовок Sec-WebSocket-Protocol: soap, wamp говорит о том, что по WebSocket браузер собирается передавать не просто какие-то данные, а данные в протоколах SOAP или WAMP («The WebSocket Application Messaging Protocol»). Стандартные подпротоколы регистрируются в специальном каталоге IANA.
Этот заголовок браузер поставит, если указать второй необязательный параметр WebSocket :
При наличии таких заголовков сервер может выбрать расширения и подпротоколы, которые он поддерживает, и ответить с ними.
В ответе выше сервер указывает, что поддерживает расширение deflate-frame , а из запрошенных подпротоколов – только SOAP.
Кроме большей безопасности, у WSS есть важное преимущество перед обычным WS – большая вероятность соединения.
А в случае с WSS весь трафик сразу кодируется и через прокси проходит уже в закодированном виде. Поэтому заголовки гарантированно пройдут, и общая вероятность соединения через WSS выше, чем через WS .
▍Функция gethostbyname
Функция gethostbyname сначала проверяет файл hosts , который, в macOS или Linux, можно найти по адресу /etc/hosts , для того, чтобы узнать, можно ли, выясняя адрес сервера, обойтись локальными сведениями.
Если локальными средствами разрешить запрос на выяснение IP-адреса сервера не удаётся, система выполняет запрос к DNS-серверу. Адреса таких серверов хранятся в настройках системы.
Вот пара популярных DNS-серверов:
- 8.8.8.8: DNS-сервер Google.
- 1.1.1.1: DNS-сервер CloudFlare.
IP-адрес, соответствующий интересующему нас доменному имени, может иметься в кэше DNS-сервера. Если это не так — он обратиться к корневому DNS-серверу. Система корневых DNS-серверов состоит из 13 серверов, от которых зависит работа всего интернета.
Получив запрос, корневой DNS-сервер перенаправляет его к DNS-серверу домена верхнего уровня, к так называемому TLD-серверу (от Top-Level Domain).
У TLD-серверов есть IP-адреса серверов имён (Name Server, NS), средствами которых и можно узнать IP-адрес по имеющемуся у нас URL. Откуда NS-сервера берут эти сведения? Дело в том, что если вы покупаете домен, доменный регистратор отправляет данные о нём серверам имён. Похожая процедура выполняется и, например, при смене хостинга.
Сервера, о которых идёт речь, обычно принадлежат хостинг-провайдерам. Как правило, для защиты от сбоев, создаются по несколько таких серверов. Например, у них могут быть такие адреса:
Теперь, после того, как нам удалось выяснить IP-адрес, стоящий за введённым в адресную строку браузера URL, мы переходим к следующему шагу нашей работы.
Итоги
Сегодня мы поговорили о механизмах сетевого взаимодействия, поддерживаемых платформой Node.js, проведя параллели с аналогичными механизмами, применяемыми в браузерах. Нашей следующей темой будет работа с файлами.
Отправка данных на сервер
Вы можете пересылать данные в виде строки, Blob , так и ArrayBuffer .
Примеры
Этот простой пример создаёт новый WebSocket, подключаемый к серверу ws://www.example.com/socketserver . В данном примере в конструктор сокета в качестве дополнительного параметра передаётся пользовательский протокол "protocolOne", хотя эта часть может быть опущена.
После выполнения функции, exampleSocket . readyState будет иметь значение CONNECTING . readyState изменится на OPEN как только соединение станет готовым к передаче данных.
Если нужно открыть соединение, поддерживающее несколько протоколов, можно передать массив протоколов:
Когда соединение установлено (что соответствует, readyState OPEN ), exampleSocket.protocol сообщит, какой протокол выбрал сервер.
▍Выполнение POST-запроса
Вот как выполнить POST-запрос из Node.js:
▍Отправка данных на сервер
После открытия WebSocket-соединения с сервером ему можно отправлять данные. Сделать это можно, например в коллбэке onopen :
▍Реализация WebSocket-сервера в среде Node.js
Для того чтобы реализовать WebSocket-сервер в среде Node.js, можно воспользоваться популярной библиотекой ws. Мы применим её для разработки сервера, но она подходит и для создания клиентов, и для организации взаимодействия между двумя серверами.
Установим эту библиотеку, предварительно инициализировав проект:
Код WebSocket-сервера, который нам надо написать, довольно-таки компактен:
Вот рабочий пример WebSocket-сервера, а вот — клиент, который может с ним взаимодействовать.
▍Получение данных с сервера
Для получения с сервера данных, отправленных с использованием протокола WebSocket, можно назначить коллбэк onmessage , который будет вызван при получении события message :
Создание объекта WebSocket
Чтобы общаться через протокол веб-сокетов необходимо создать объект WebSocket ; при его создании автоматически происходит попытка открыть соединение с сервером.
Конструктор WebSocket принимает один обязательный и один необязательный параметр:
url URL, с которым происходит соединение; это должен быть URL веб-сокет-сервера. protocols Необязательный Может быть одной строкой протокола или массивом таких строк. Эти строки используют для индикации под-протоколов; таким образом, один сервер может реализовывать несколько под-протоколов веб-сокетов (к примеру, вам может потребоваться, чтобы сервер мог обрабатывать разные типы взаимодействий в зависимости от определённого под-протокола). Если вы не укажете строку протокола, то будет передана пустая строка.
В конструкторе могут возникать следующие исключения:
SECURITY_ERR Порт, к которому проводится подключение, заблокирован.
О создании простого сервера средствами Node.js
Теперь, после того, как мы разобрали процесс взаимодействия браузера и сервера, вы можете по-новому взглянуть на раздел Первое Node.js-приложение из первой части этой серии материалов, в котором мы описывали код простого сервера.
Простой пример
Чтобы открыть веб-сокет-соединение, нам нужно создать объект new WebSocket , указав в url-адресе специальный протокол ws :
Протокол wss:// не только использует шифрование, но и обладает повышенной надёжностью.
Это потому, что данные ws:// не зашифрованы, видны для любого посредника. Старые прокси-серверы не знают о WebSocket, они могут увидеть «странные» заголовки и закрыть соединение.
Как только объект WebSocket создан, мы должны слушать его события. Их всего 4:
Для демонстрации есть небольшой пример сервера server.js, написанного на Node.js, для запуска примера выше. Он отвечает «Привет с сервера, Джон», после ожидает 5 секунд и закрывает соединение.
Так вы увидите события open → message → close .
В общем-то, всё, мы уже можем общаться по протоколу WebSocket. Просто, не так ли?
Теперь давайте поговорим более подробно.
Получение и интерпретация JSON объектов
Давайте рассмотрим клиентское приложение чата, которое впервые упоминалось в разделе Использование JSON для передачи объектов. Есть разные типы пакетов данных, которые может получить клиент, например:
Здесь мы используем JSON.parse() чтобы преобразовать JSON строку в объект, затем обработайте его.
PING / PONG
В протокол встроена проверка связи при помощи управляющих фреймов типа PING и PONG.
Тот, кто хочет проверить соединение, отправляет фрейм PING с произвольным телом. Его получатель должен в разумное время ответить фреймом PONG с тем же телом.
Эта функциональность встроена в браузерную реализацию, так что браузер ответит на PING сервера, но управлять ей из JavaScript нельзя.
Иначе говоря, сервер всегда знает, жив ли посетитель или у него проблема с сетью.
Использование JSON для передачи объектов
Одна удобная вещь которую вы можете сделать, это использовать JSON для пересылки сложных данных на сервер. Например, приложение-чат может взаимодействовать с сервером, используя протокол, реализованный с использованием пакетов данных, инкапсулированных в JSON:
Чистое закрытие
При закрытии соединения сторона, желающая это сделать (обе стороны в WebSocket равноправны) отправляет закрывающий фрейм (опкод 0x8 ), в теле которого указывает причину закрытия.
В браузерной реализации эта причина будет содержаться в свойстве reason события onclose .
Наличие такого фрейма позволяет отличить «чистое закрытие» от обрыва связи.
В браузерной реализации событие onclose при чистом закрытии имеет event.wasClean = true .
Пример браузерного кода
Для открытия соединения достаточно создать объект WebSocket , указав в нём специальный протокол ws .:
У объекта socket есть четыре колбэка: один при получении данных и три – при изменениях в состоянии соединения:
Для посылки данных используется метод socket.send(data) . Пересылать можно любые данные.
…Или файл, выбранный в форме:
Просто, не правда ли? Выбираем, что переслать, и socket.send() .
Для того, чтобы коммуникация была успешной, сервер должен поддерживать протокол WebSocket.
Чтобы лучше понимать происходящее – посмотрим, как он устроен.
▍Установление TCP-соединения
Открытие веб-сокета
Когда new WebSocket(url) создан, он тут же сам начинает устанавливать соединение.
Вот пример заголовков для запроса, который делает new WebSocket("wss://javascript.info/chat") .
Здесь Sec-WebSocket-Accept – это Sec-WebSocket-Key , перекодированный с помощью специального алгоритма. Браузер использует его, чтобы убедиться, что ответ соответствует запросу.
▍Фаза DNS-поиска
Итак, браузер, начиная работу по загрузке данных с запрошенного пользователям адреса, выполняет операцию DNS-поиска (DNS Lookup) для того, чтобы выяснить IP-адрес соответствующего сервера. Символьные имена ресурсов, вводимые в адресную строку, удобны для людей, но устройство интернета подразумевает возможность обмена данными между компьютерами с использованием IP-адресов, которые представляют собой наборы чисел наподобие 222.324.3.1 (для протокола IPv4).
Если в кэше ничего найти не удаётся, браузер использует системный вызов POSIX gethostbyname для того, чтобы узнать IP-адрес сервера.
Примеры
В заголовке первый байт содержит FIN=1 и опкод=0x1 (получается 10000001 в двоичной системе, то есть 0x81 – в 16-ричной), далее идёт длина 0x5 , далее текст.
А теперь посмотрим на все те замечательные возможности, которые даёт этот формат фрейма.
Читайте также: