web сокеты что это
Асинхронный веб, или Что такое веб-сокеты
Авторизуйтесь
Асинхронный веб, или Что такое веб-сокеты
Веб-сокеты (Web Sockets) — это передовая технология, которая позволяет создавать интерактивное соединение между клиентом (браузером) и сервером для обмена сообщениями в режиме реального времени. Веб-сокеты, в отличие от HTTP, позволяют работать с двунаправленным потоком данных, что делает эту технологию совершенно уникальной. Давайте разберемся, как работает эта технология и чем она отличается от HTTP.
Как работает HTTP?
Схема обмена сообщениями по HTTP
Вы наверняка знаете, что такое HTTP (или HTTPS), поскольку встречаетесь с этим протоколом каждый день в своём браузере. Браузер постоянно спрашивает у сервера, есть ли для него новые сообщения, и получает их.
Вы также можете знать, что HTTP позволяет использовать разные типы запросов, такие как POST, GET или PUT, каждый из которых имеет своё назначение.
Как работают веб-сокеты?
Схема обмена сообщениями при использовании веб-сокетов
Веб-сокетам же для ответа не нужны ваши повторяющиеся запросы. Достаточно выполнить один запрос и ждать отклика. Вы можете просто слушать сервер, который будет отправлять вам сообщения по мере готовности.
Веб-сокеты можно использовать, если вы разрабатываете:
Когда следует избегать использования веб-сокетов?
Практически никогда. Единственный минус — это несовместимость с некоторыми браузерами, но уже 95 % браузеров поддерживают веб-сокеты.
3–4 декабря, Онлайн, Беcплатно
В некоторых случаях веб-сокеты вам всё же не понадобятся. Если вы создаёте простую CMS, вам вряд ли пригодится функциональность в режиме реального времени. Также не стоит использовать веб-сокеты в REST API, поскольку вам хватит таких HTTP-запросов, как GET, POST, DELETE и PUT.
Практические примеры
В примерах ниже для клиента используется JavaScript, а для сервера — Node.js. Примеры очень просты и вряд ли пригодятся на практике, но зато позволят разобраться в сути.
Веб-сокеты
Вот иллюстрация работы веб-сокетов:
Демонстрация работы веб-сокетов
Эквивалент в HTTP
Так как HTTP должен постоянно проверять канал на наличие новых сообщений, можно использовать «грязную» проверку (dirty check) — подход, при котором клиент с заданной периодичностью (допустим, каждые 200 мс) проверяет наличие новых сообщений на сервере.
Чтобы не вникать в XMLHttpRequest, можно использовать библиотеку Axios. Она декларативна и очень понятна.
Заключение
Веб-сокеты являются одной из самых перспективных веб-технологий, которую уже сейчас используют многие разработчики. Она отлично подходит для взаимодействия в режиме реального времени, в том числе в онлайн-играх.
WebSockets — полноценный асинхронный веб
Пару недель назад разработчики Google Chromium опубликовали новость о поддержке технологии WebSocket. В айтишном буржунете новость произвела эффект разорвавшейся бомбы. В тот же день различные очень известные айтишники опробовали новинку и оставили восторженные отзывы в своих блогах. Моментально разработчики самых разных серверов/библиотек/фреймворков (в их числе Apache, EventMachine, Twisted, MochiWeb и т.д.) объявили о том, что поддержка ВебСокетов будет реализована в их продуктах в ближайшее время.
Что же такого интересного сулит нам технология? На мой взгляд, WebSocket — это самое кардинальное расширение протокола HTTP с его появления. Это не финтифлюшки, это сдвиг парадигмы HTTP. Изначально синхронный протокол, построенный по модели «запрос — ответ», становится полностью асинхронным и симметричным. Теперь уже нет клиента и сервера с фиксированными ролями, а есть два равноправных участника обмена данными. Каждый работает сам по себе, и когда надо отправляет данные другому. Отправил — и пошел дальше, ничего ждать не надо. Вторая сторона ответит, когда захочет — может не сразу, а может и вообще не ответит. Протокол дает полную свободу в обмене данными, вам решать как это использовать.
Я считаю, что веб сокеты придутся ко двору, если вы разрабатываете:
— веб-приложения с интенсивным обменом данными, требовательные к скорости обмена и каналу;
— приложения, следующие стандартам;
— «долгоиграющие» веб-приложения;
— комплексные приложения со множеством различных асинхронных блоков на странице;
— кросс-доменные приложения.
И как это работает?
Очень просто! Как только ваша страница решила, что она хочет открыть веб сокет на сервер, она создает специальный javascript-объект:
А что при этом происходит в сети?
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: site.com
Origin: http://site.com
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://site.com
WebSocket-Location: ws://site.com/demo
Если браузер это устраивает, то он просто оставляет TCP-соединение открытым. Все — «рукопожатие» совершено, канал обмена данными готов.
Как только одна сторона хочет передать другой какую-то информацию, она отправляет дата-фрейм следующего вида:
То есть просто строка текста — последовательность байт, к которой спереди приставлен нулевой байт 0x00, а в конце — 0xFF. И все — никаких заголовков, метаданных! Что именно отправлять, разработчики полностью оставили на ваше усмотрение: хотите XML, хотите JSON, да хоть стихи Пушкина.
Каждый раз, когда браузер будет получать такое сообщение, он будет «дергать» ваш колл-бек onmessage.
Легко понять, что КПД такого протокола стремится к 95%. Это не классический AJAX-запрос, где на каждую фитюльку приходится пересылать несколько килобайт заголовков. Разница будет особенно заметна если делать частый обмен небольшими блоками данных. Скорость обработки так же стремится к скорости чистого TCP-сокета — ведь все уже готово — соединение открыто — всего лишь байты переслать.
А картинку можно отправить?
Что значит «один или несколько байт»? Чтобы не создавать ограничений на длину передаваемого сообщения и в тоже время не расходовать байты нерационально, разработчики использовали очень хитрый способ указания длины тела сообщения. Каждый байт в указании длины рассматривается по частям: самый старший бит указывает является ли этот байт последним (0) либо же за ним есть другие (1), а младшие 7 битов содержат собственно данные. Обрабатывать можно так: как только вы получили признак бинарного дата-фрейма 0x80, вы берете следующий байт и откладываете его в отдельную «копилку», смотрите на следующий байт — если у него установлен старший бит, то переносите и его в «копилку», и так далее, пока вам не встретится байт с 0 старшим битом. Значит это последний байт в указателе длины — его тоже переносите в «копилку». Теперь из всех байтов в «копилке» убираете старшие биты и слепляете остаток. Вот это и будет длина тела сообщения. Еще можно интерпретировать как 7-битные числа без старшего бита.
Например, самую главную картинку веб-дизайна — прозначный однопиксельный GIF размером 43 байта можно передать так:
Не правда ли, очень элегантно?
Что это нам дает?
Скорость и эффективность
Высокую скорость и эффективность передачи обеспечивает малый размер передаваемых данных, который иногда даже будет помещаться в один TCP-пакет — здесь, конечно, же все зависит от вашей бизнес-логики. (В дата-фрейм можно засунуть и БСЭ, но для такой передачи потребуется чуть больше 1 TCP- пакета. 🙂 ).
Так же учтите, что соединение уже готово — не надо тратить время и трафик на его установление, хендшейки, переговоры.
Стандартность
Самим своим выходом WebSockets отправит на свалку истории Comet и все приблуды накрученные поверх него — Bayuex, LongPolling, MultiPart и так далее. Это все полезные технологии, но по большей части, они работают на хаках, а не стандартах. Отсюда периодески возникают проблемы: то прокся ответ «зажевала» и отдала его только накопив несколько ответов. Для «пробивания» проксей часто использовался двух-килобайтный «вантуз» — т.е. объем передаваемых данных увеличивался пробелами (или другими незначащими символами) до 2К, которые многие прокси передавали сразу, не задерживая. Периодически антивирусы выражали свое желание получить ответ полностью, проверить его, и только потом передать получателю. Конечно, сегодня все эти проблемы более-менее решены — иначе бы не было такого большого кол-ва веб-приложений. Однако, развитие в этом направлении сопряжено с появлением новых проблем — именно потому, что это попытка делать в обход стандарта.
Время жизни канала
В отличие от HTTP веб-сокеты не имеют ограничений на время жизни в неактивном состоянии. Это значит, что больше не надо периодически рефрешить соединение, т.к. его не вправе «прихлопывать» всякие прокси. Значит, соединение может висеть в неактивном виде и не требовать ресурсов. Конечно, можно возразить, что на сервере будут забиваться TCP-сокеты. Для этого достаточно использовать хороший мультиплексор, и нормальный сервер легко потянет до миллиона открытых коннектов.
Комплексные веб-приложения
Как известно в HTTP предусмотрено ограничение на число одновременных октрытых сессий к одному серверу. Из-за этого если у вас много различных асинхронных блоков на странице, то вам приходилось делать не только серверный, но и клиентский мультиплексор — именно отсюда идет Bayeux Protocol.
К счастью, это ограничение не распространяется на веб-сокеты. Открываете столько, сколько вам нужно. А сколько использовать — одно (и через него все мультиплексировать) или же наоборот — на каждый блок свое соединение — решать вам. Исходите из удобства разработки, нагрузки на сервер и клиент.
Кросс-доменные приложения
И еще один «камень в ботинке» AJAX-разработчика — проблемы с кросс-доменными приложениями. Да, и для них тоже придумана масса хаков. Помашем им ручкой и смахнем скупую слезу. WebSockets не имеет таких ограничений. Ограничения вводятся не по принципу «из-того-же-источника», а из «разрешенного-источника», и определяются не на клиенте, а на сервере. Думаю, внимательные уже заметили новый заголовок Origin. Через него передается информация откуда хотят подключиться к вашему websocket-у. Если этот адрес вас не устраивает, то вы отказываете в соединение.
Все! Конец кросс-доменной зопяной боли!
А руками пощупать можно?
UPDATE: Одной из открытых реализаций веб-сокетов является чат на www.mibbit.com (заметка в их блоге).
PHP-реализация сервера WebSocket также представлена модулем к асинхронному фреймворку phpDaemon, модуль называется WebSocketServer. Пример простейшего приложения, которое отвечает «pong» на фрейм (пакет) «ping» — ExampleWebSocket.
Вы можете попутно прослушать соедиение с помощью например tcpdump или любой другой программы и увидеть в действии всю ту механику, которую я описал выше.
Светлое будущее
И когда же оно настанет? На самом деле очень скоро. Гугл в очередной раз дал «волшебного пендаля» всей веб-индустрии, и все зашевелились. Вы удивитесь, но тут же люди вспомнили, что в багзилле фаерфокса уже год(!) висит задача на эту тему. В Хроме все изменения сделаны в WebKit — а значит очень скоро появится поддержка в Safari. Скоро подтянутся и остальные браузеры.
А если нельзя, но очень хочется?
На этот случай придуман временный заменитель — библиотечка web-socket-js с помощью флеша эмулирующая веб-сокеты. К сожалению, у нее есть небольшие проблемы с проксями и кросс-доменной работой. Но в качестве временного решения ее стоит опробовать.
Выводы
На мой взгляд, как только люди распробуют, эта технология получить очень широкое распространение. К весне-лету мы получим массу сайтов с ней. И как в свое время несколько лет прошло «под звездой AJAX», так и здесь год-другой мы будем слышать отзывы о внедрении WebSockets повсеместно.
Осторожно, двери закрываются. Не опоздайте на поезд в будущее.
Разница между веб-сокетами и Socket.IO
Доброго времени суток, друзья!
Веб-сокеты и Socket.IO, вероятно, являются двумя наиболее распространенными средствами коммуникации в режиме реального времени (далее — живое общение). Но чем они отличаются?
При построении приложения для живого общения наступает момент, когда необходимо выбрать средство для обмена данными между клиентом и сервером. Веб-сокеты и Socket.IO являются самыми популярными средствами живого общения в современном вебе. Какое из них выбрать? В чем разница между этими технологиями? Давайте выясним.
Веб-сокеты
Говоря о веб-сокетах, мы имеем ввиду протокол веб-коммуникации, представляющий полнодуплексный канал коммуникации поверх простого TCP-соединения. Проще говоря, эта технология позволяет установить связь между клиентом и сервером с минимальными затратами, позволяя создавать приложения, использующие все преимущества живого общения.
Например, представьте, что вы создаете чат: вам необходимо получать и отправлять данные как можно быстрее, верно? С этим прекрасно справляются веб-сокеты! Вы можете открыть TCP-соединение и держать его открытым сколько потребуется.
Веб-сокеты появились в 2010 году в Google Chrome 4, первый RFC (6455) опубликован в 2011.
Веб-сокеты используются в следующих случаях:
Socket.IO
Socket.IO — библиотека JavaScript, основанная (написанная поверх) на веб-сокетах… и других технологиях. Она использует веб-сокеты, когда они доступны, или такие технологии, как Flash Socket, AJAX Long Polling, AJAX Multipart Stream, когда веб-сокеты недоступны. Легкой аналогией может служить сравнение Fetch API и Axios.
Разница между веб-сокетами и Socket.IO
Главными преимуществами Socket.IO является следующее:
Во-первых, веб-сокеты поддерживаются всеми современными браузерами. Поэтому вы редко нуждаетесь в поддержке других технологий, предоставляемой Socket.IO.
Если говорить о сетевом трафике, то веб-сокеты отправляют всего два запроса:
В npm существует пакет «websocket-vs-socket.io», который позволяет сравнить сетевой трафик этих технологий:
Сетевой трафик веб-сокетов:
Сетевой трафик Socket.IO:
Пишем код
Простой сервер на веб-сокетах
В нашей программе на Node.js мы создадим сервер, работающий на порту 3001. При каждом подключении клиента мы будем присваивать ему уникальный ID. При отправке сообщения клиентом мы будем уведомлять его об успехе: [ ]:
Отлично! Но что если мы хотим рассылать сообщение каждому подключенному клиенту? Веб-сокеты не поддерживают рассылку по умолчанию. Это можно реализовать следующим образом:
Легко и просто! Как видите, WebSocket.Server хранит записи о каждом подключенном клиенте, поэтому мы можем сделать итерацию и отправить сообщение каждому. Вы можете протестировать код на компьютере (MacOS) или в браузере (Chrome).
Простой сервер на Socket.IO
Это было не сложно. Может ли Socket.IO сделать это еще проще? Как нам написать такой же сервер на Socket.IO?
Код получился почти наполовину короче! Как видите, метод «broadcast» не отправляет уведомление отправителю, поэтому мы вынуждены делать это вручную.
Существует проблема: код нельзя протестировать на обычном клиенте веб-сокетов. Это связано с тем, что, как отмечалось ранее, Socket.IO использует не чистые веб-сокеты, а множество технологий для поддержки всех возможных клиентов. Так как же нам проверить его работоспособность?
Необходимо использовать специальный клиент. В приведенном примере мы загружаем его из CDN. Этот клиент позволяет нам провести быстрые (грязные) тесты в браузере.
Вебсокеты: боевое применение
Вебсокеты — это прогрессивный стандарт полнодуплексной (двусторонней) связи с сервером по TCP-соединению, совместимый с HTTP. Он позволяет организовывать живой обмен сообщениями между браузером и веб-сервером в реальном времени, причем совершенно иным способом, нежели привычная схема «запрос URL — ответ». Когда два года назад я присматривался к этому стандарту, он был еще в зачаточном состоянии. Существовал лишь неутвержденный набросок черновика и экспериментальная поддержка некоторыми браузерами и веб-серверами, причем в Файрфоксе он был по умолчанию отключен из-за проблем с безопасностью. Однако теперь ситуация изменилась. Стандарт приобрел несколько ревизий (в том числе без обратной совместимости), получил статус RFC (6455) и избавился от детских болезней. Во всех современных браузерах, включая IE10, заявлена поддержка одной из версий протокола, и есть вполне готовые к промышленному использованию веб-серверы.
Я решил, что настало время попробовать это на живом проекте. И теперь делюсь, что из этого вышло.
Суть задачи
На моем личном небольшом сайте Клавогонки.ру есть центральная часть — список действующих в данных момент игр-заездов. Список крайне динамичный: новый заезд создается игроками каждые несколько секунд, иногда несколько в секунду. Заезды начинаются с отсчета времени, по завершению перемещаются из раздела открытых игр в раздел активных. После выхода всех игроков заезд убирается со страницы. В один заезд заходит от одного и иногда до ста игроков, что требуется отображать тут же.
Как это работало раньше
Изначально, когда появилась необходимость сделать эту часть функциональности сайта, возникло много трудностей с динамическим обновлением списка. На странице списка могут находиться десятки человек одновременно, каждый из которых хочет видеть свежую картину. У многих заездов может быть таймаут отсчета от создания до старта всего 10-20 секунд, и чтобы успеть присоединиться к ним, обновление должно происходить достаточно живо.
Обновление всей страницы по таймауту здесь не подходило вообще никак, и нужно было искать другие варианты (тут надо сделать ремарку, что использовать флеш на сайте не хотелось без очень сильной на то необходимости).
Самым очевидным и простым решением здесь на первый взгляд казался long-polling — висящее подключение к серверу, которое обрывается в момент поступления нового события и переоткрывается заново. Однако после некоторых тестов этот вариант тоже оказался нежизнеспособным: события поступали непрерывным потоком, ведь клиенту нужно сообщать не только о создании новой игры, но и об изменении параметров всех игр (например, старт таймаута, смена статуса, состава игроков), и количество запросов начало вызывать определенную степень недовольства у сервера. Да и оверхед на открытие-закрытие запросов тоже выходил немаленький.
HTTP-streaming не получилось использовать из-за проблем с прокси-серверами у многих пользователей.
Поэтому я остановился на простом варианте обновления содержимого страницы по таймауту раз в 3 секунды через ajax-запросы. На сервере текущие данные кешировались и отдавались клиентам из кэша в json, при этом для экономии трафика отдавался не весь список каждый раз, а лишь измененные данные через систему версионирования (увеличилась версия по сравнению с запрашиваемой — отдаем новую информацию о заезде, иначе отдаем только текущий номер версии).
Система показала себя неплохо и проработала долгое время. Однако был большой минус — очень трудно зайти в заезд с 10-секундным таймаутом до старта. Кроме того, это совсем не соответствовало духу динамичной гоночной онлайн-игры и выглядело не слишком технологично в целом.
Увидеть эту страницу в ее старом варианте вы можете по этой ссылке.
Как это работает сейчас
Если говорить кратко, вебсокеты позволили внести драйв в весь этот процесс.
Для начала был выбран сервер, который должен жить в связке с действующим игровым бэкэндом. По ряду причин я выбрал для этого node.js — событийно-ориентированная модель и хорошо развитые коллбэки в javascript идеально подошли для этой задачи.
Общей средой общения между php-бэкэндом и сервером на node.js стали pubsub-каналы redis. При создании новой игры или любом действии, изменяющем данные, php делает примерно следующее (код здесь и далее сильно упрощен):
Redis работает как отдельный демон на отдельном TCP-порте и принимает/рассылает сообщения от любого количества подключенных клиентов. Это дает возможность хорошо масштабировать систему, невзирая на количество процессов (ну и серверов, если думать оптимистично) php и node.js. Сейчас крутится примерно 50 php-процессов и 2 node.js-процесса.
На стороне node.js при старте идет подключение к прослушке redis-канала под названием gamelist :
Для работы с клиентами используется обвязочная библиотека Socket.IO (upd: товарищи Voenniy и Joes в комментах говорят, что есть более качественные альтернативы вроде SockJS и Beseda, что вполне может быть правдой). Она позволяет использовать вебсокеты как основной транспорт, откатываясь при этом на другие транспорты вроде флеша или xhr-polling если браузер не поддерживает вебсокеты. Вдобавок, она упрощает работу с клиентами в целом, например дает API для мультиплексирования и разделения подключенных клиентов по разным псевдокаталогам (каналам), позволяет именовать события и некоторые другие плюшки.
При поступлении по redis-каналу события из бэкэнда оно всячески предварительно анализируется и потом отсылается всем подключенным клиентам в gamelistSockets :
Браузер получает событие ровно таким же образом и рендерит необходимые изменения на странице.
Принцип совершенно прост и ясен. Продвинутые технологии в основе этой схемы позволяют сильно упростить процесс и сосредоточиться на логике самой работы. Хотя пришлось несколько повозиться с переделкой некоторых частей php-кода для работы в идеологии «сообщаем об изменении, а не о состоянии», а также вынести домен вебсокетов на отдельную от основной машину (чтобы не мучиться с разделяющим прокси на 80 порту), но в сухом остатке плюсы вышли очень существенными:
Посмотреть на то, что получилось в итоге, вы можете здесь. Разница видна невооруженным взглядом.
В качестве бонуса две таблички, небольшая статистика по аудитории Клавогонок, браузеры и используемые в Socket.IO транспорты:
Браузер | Доля | Транспорт | Доля |
---|---|---|---|
Chrome | 51% | websocket | 90% |
Firefox | 20% | xhr-polling | 5% |
Opera | 15% | flashsocket | 4% |
IE (примерно пополам 8 и 9) | 6% | jsonppolling | 1% |
Как видно, вполне готово к употреблению.
Тут могла бы быть заключительная резюмирующая часть с итогами, библиографией и моралью. Но я сэкономлю ваше время и скажу просто: вебсокеты — это очень круто!
Как работает JS: WebSocket и HTTP/2+SSE. Что выбрать?
В этот раз мы поговорим о коммуникационных протоколах, сопоставим и обсудим их особенности и составные части. Тут мы займёмся технологиями WebSocket и HTTP/2, в частности, поговорим о безопасности и поделимся советами, касающимися выбора подходящих протоколов в различных ситуациях.
Введение
В наши дни сложные веб-приложения, обладающие насыщенными динамическими пользовательскими интерфейсами, воспринимаются как нечто само собой разумеющееся. А ведь интернету пришлось пройти долгий путь для того, чтобы достичь его сегодняшнего состояния.
В самом начале интернет не был рассчитан на поддержку подобных приложений. Он был задуман как коллекция HTML-страниц, как «паутина» из связанных друг с другом ссылками документов. Всё было, в основном, построено вокруг парадигмы HTTP «запрос/ответ». Клиентские приложения загружали страницы и после этого ничего не происходило до того момента, пока пользователь не щёлкнул мышью по ссылке для перехода на очередную страницу.
Примерно в 2005-м году появилась технология AJAX и множество программистов начало исследовать возможности двунаправленной связи между клиентом и сервером. Однако, все сеансы HTTP-связи всё ещё инициировал клиент, что требовало либо участия пользователя, либо выполнения периодических обращений к серверу для загрузки новых данных.
«Двунаправленный» обмен данными по HTTP
Технологии, которые позволяют «упреждающе» отправлять данные с сервера на клиент существуют уже довольно давно. Среди них — Push и Comet.
Один из наиболее часто используемых приёмов для создании иллюзии того, что сервер самостоятельно отправляет данные клиенту, называется «длинный опрос» (long polling). С использованием этой технологии клиент открывает HTTP-соединение с сервером, который держит его открытым до тех пор, пока не будет отправлен ответ. В результате, когда у сервера появляются данные для клиента, он их ему отправляет.
Вот пример очень простого фрагмента кода, реализующего технологию длинного опроса:
Эта конструкция представляет собой функцию, которая сама себя вызывает после того, как, в первый раз, будет запущена автоматически. Она задаёт 10-секундный интервал для каждого асинхронного Ajax-обращению к серверу, а после обработки ответа сервера снова выполняется планирование вызова функции.
Ещё одна используемая в подобной ситуации техника — это Flash или составной запрос HXR, и так называемые htmlfiles.
У всех этих технологий одна и та же проблема: дополнительная нагрузка на систему, которую создаёт использование HTTP, что делает всё это неподходящим для организации работы приложений, где требуется высокая скорость отклика. Например, это что-то вроде многопользовательской браузерной «стрелялки» или любой другой онлайн-игры, действия в которой выполняются в режиме реального времени.
Введение в технологию WebSocket
Спецификация WebSocket определяет API для установки соединения между веб-браузером и сервером, основанного на «сокете». Проще говоря, это — постоянное соединение между клиентом и сервером, пользуясь которыми клиент и сервер могут отправлять данные друг другу в любое время.
Посмотрим, как установка такого соединения выглядит со стороны клиента:
Вот упрощённый пример заголовков исходного запроса.
После установки соединения в ответе сервера будут сведения о переходе на протокол WebSocket:
После этого вызывается событие open в экземпляре WebSocket на клиенте:
Теперь, после завершения фазы рукопожатия, исходное HTTP-соединение заменяется на WebSocket-соединение, которое использует то же самое базовое TCP/IP-соединение. В этот момент и клиент и сервер могут приступать к отправке данных.
Благодаря использованию WebSocket можно отправлять любые объёмы данных, не подвергая систему ненужной нагрузке, вызываемой использованием традиционных HTTP-запросов. Данные передаются по WebSocket-соединению в виде сообщений, каждое из которых состоит из одного или нескольких фреймов, содержащих отправляемые данные (полезную нагрузку). Для того, чтобы обеспечить правильную сборку исходного сообщения по достижению им клиента, каждый фрейм имеет префикс, содержащий 4-12 байтов данных о полезной нагрузке. Использование системы обмена сообщениями, основанной на фреймах, помогает сократить число служебных данных, передаваемых по каналу связи, что значительно уменьшает задержки при передаче информации.
Стоит отметить, что клиенту будет сообщено о поступлении нового сообщения только после того, как будут получены все фреймы и исходная полезная нагрузка сообщения будет реконструирована.
Различные URL протокола WebSocket
При построении URL-адресов используются определённые правила. Особенностью URL WebSocket является то, что они не поддерживают якоря ( #sample_anchor ).
В остальном к URL WebSocket применяются те же правила, что и к URL HTTP. При использовании ws-адресов соединение оказывается незашифрованным, по умолчанию применяется порт 80. При использовании wss требуется TLS-шифрование и применяется порт 443.
Протокол работы с фреймами
Взглянем поближе на протокол работы с фреймами WebSocket. Вот что можно узнать о структуре фрейма из соответствующего RFC:
Если говорить о версии WebSocket, стандартизированной RFC, то можно сказать, что в начале каждого пакета имеется небольшой заголовок. Однако, устроен он довольно сложно. Вот описание его составных частей:
Данные во фреймах
API для передачи данных по протоколу WebSocket устроено очень просто:
Узнать, что находится внутри фреймов WebSocket-соединения, можно с помощью вкладки Network (Сеть) инструментов разработчика Chrome:
Фрагментация данных
Логика объединения фреймов, в общих чертах, выглядит так:
Благодаря фрагментации сервер может подобрать буфер разумного размера, а, когда буфер заполняется, отправлять данные в сеть. Второй вариант использования фрагментации заключается в мультиплексировании, когда нежелательно, чтобы сообщение занимало весь логический канал связи. В результате для целей мультиплексирования нужно иметь возможность разбивать сообщения на более мелкие фрагменты для того, чтобы лучше организовать совместное использование канала.
О heartbeat-сообщениях
В любой момент после процедуры рукопожатия, либо клиент, либо сервер, может решить отправить другой стороне ping-сообщение. Получая такое сообщение, получатель должен отправить, как можно скорее, pong-сообщение. Это и есть heartbeat-сообщения. Их можно использовать для того, чтобы проверить, подключён ли ещё клиент к серверу.
Подобная схема обмена сообщениями может быть очень полезной. Есть службы (вроде балансировщиков нагрузки), которые останавливают простаивающие соединения.
Вдобавок, одна из сторон не может, без дополнительных усилий, узнать о том, что другая сторона завершила работу. Только при следующей отправке данных вы можете выяснить, что что-то пошло не так.
Обработка ошибок
Закрытие соединения
Вот как инициируют операцию закрытия WebSocket-соединения на клиенте:
Кроме того, для того, чтобы произвести очистку после завершения закрытия соединения, можно подписаться на событие close :
Серверу нужно прослушивать событие close для того, чтобы, при необходимости, его обработать:
Сравнение технологий WebSocket и HTTP/2
Хотя HTTP/2 предлагает множество возможностей, эта технология не может полностью заменить существующие push-технологии и потоковые способы передачи данных.
Первое, что важно знать об HTTP/2, заключается в том, что это — не замена всего, что есть в HTTP. Виды запросов, коды состояний и большинство заголовков остаются такими же, как и при использовании HTTP. Новшества HTTP/2 заключаются в повышении эффективности передачи данных по сети.
Если сравнить HTTP/2 и WebSocket, мы увидим много общих черт.
Показатель | HTTP/2 | WebSocket |
Сжатие заголовков | Да (HPACK) | Нет |
Передача бинарных данных | Да | Да (бинарные или текстовые) |
Мультиплексирование | Да | Да |
Приоритизация | Да | Нет |
Сжатие | Да | Да |
Направление | Клиент/Сервер и Server Push | Двунаправленная передача данных |
Полнодуплексный режим | Да | Да |
Как уже было сказано, HTTP/2 вводит технологию Server Push, которая позволяет серверу отправлять данные в клиентский кэш по собственной инициативе. Однако, при использовании этой технологии данные нельзя отправлять прямо в приложение. Данные, отправленные сервером по своей инициативе, обрабатывает браузер, при этом нет API, которые позволяют, например, уведомить приложение о поступлении данных с сервера и отреагировать на это событие.
Именно в подобной ситуации весьма полезной оказывается технология Server-Sent Events (SSE). SSE — это механизм, который позволяет серверу асинхронно отправлять данные клиенту после установления клиент-серверного соединения.
Так как технология SSE основана на HTTP, она отлично сочетается с HTTP/2. Её можно скомбинировать с некоторыми возможностями HTTP/2, что открывает дополнительные перспективы. А именно, HTTP/2 даёт эффективный транспортный уровень, основанный на мультиплексированных каналах, а SSE даёт приложениям API для передачи данных с сервера.
Для того, чтобы в полной мере понять возможности мультиплексирования и потоковой передачи данных, взглянем на определение IETF: «поток» — это независимая, двунаправленная последовательность фреймов, передаваемых между клиентом и сервером в рамках соединения HTTP/2. Одна из его основных характеристик заключается в том, что одно HTTP/2-соединение может содержать несколько одновременно открытых потоков, причём, любая конечная точка может обрабатывать чередующиеся фреймы из нескольких потоков.
Технология SSE основана на HTTP. Это означает, что с использованием HTTP/2 не только несколько SSE-потоков могут передавать данные в одном TCP-соединении, но то же самое может быть сделано и с комбинацией из нескольких наборов SSE-потоков (отправка данных клиенту по инициативе сервера) и нескольких запросов клиента (уходящих к серверу).
Благодаря HTTP/2 и SSE теперь имеется возможность организации двунаправленных соединений, основанных исключительно на возможностях HTTP, и имеется простое API, которое позволяет обрабатывать в клиентских приложениях данные, поступающие с серверов. Недостаточные возможности в сфере двунаправленной передачи данных часто рассматривались как основной недостаток при сравнении SSE и WebSocket. Благодаря HTTP/2 подобного недостатка больше не существует. Это открывает возможности по построению систем обмена данными между серверными и клиентскими частями приложений исключительно с использованием возможностей HTTP, без привлечения технологии WebSocket.
WebSocket и HTTP/2. Что выбрать?
Несмотря на чрезвычайно широкое распространение связки HTTP/2+SSE, технология WebSocket, совершенно определённо, не исчезнет, в основном из-за того, что она отлично освоена и из-за того, что в весьма специфических случаях у неё есть преимущества перед HTTP/2, так как она была создана для обеспечения двустороннего обмена данными с меньшей дополнительной нагрузкой на систему (например, это касается заголовков).
Предположим, вы хотите создать онлайн-игру, которая нуждается в передаче огромного количества сообщений между клиентами и сервером. В подобном случае WebSocket подойдёт гораздо лучше, чем комбинация HTTP/2 и SSE.
В целом, можно порекомендовать использование WebSocket для случаев, когда нужен по-настоящему низкий уровень задержек, приближающийся, при организации связи между клиентом и сервером, к обмену данными в реальном времени. Помните, что такой подход может потребовать переосмысления того, как строится серверная часть приложения, а также то, что тут может потребоваться обратить внимание на другие технологии, вроде очередей событий.
Если вам нужно, например, показывать пользователям в реальном времени новости или рыночные данные, или вы создаёте чат-приложение, использование связки HTTP/2+SSE даст вам эффективный двунаправленный канал связи, и, в то же время — преимущества работы с технологиями из мира HTTP. А именно, технология WebSocket нередко становится источником проблем, если рассматривать её с точки зрения совместимости с существующей веб-инфраструктурой, так как её использование предусматривает перевод HTTP-соединения на совершенно другой протокол, ничего общего с HTTP не имеющий. Кроме того, тут стоит учесть соображения масштабируемости и безопасности. Компоненты веб-систем (файрволы, средства обнаружения вторжений, балансировщики нагрузки) создают, настраивают и поддерживают с оглядкой на HTTP. В результате, если говорить об отказоустойчивости, безопасности и масштабируемости, для больших или очень важных приложений лучше подойдёт именно HTTP-среда.
Кроме того, во внимание стоит принять и поддержку технологий браузерами. Посмотрим, как с этим дела обстоят у WebSocket:
Тут всё выглядит очень даже прилично. Однако, в случае с HTTP/2 всё уже не так:
Тут можно отметить следующие особенности поддержки HTTP/2 в разных браузерах:
Не поддерживают эту технологию лишь IE/Edge. (Да, Opera Mini не поддерживает ни SSE, ни WebSocket, поэтому поддержку в этом браузере мы можем, сравнивая эти технологии, и не учитывать.) Однако, для IE/Edge существуют достойные полифиллы.
Итоги
Как видите, у технологий WebSockets и HTTP/2+SSE есть, в сравнении друг с другом, и преимущества, и недостатки. Что же всё-таки выбрать? Пожалуй, на этот вопрос поможет ответить лишь анализ конкретного проекта и всесторонний учёт его требований и особенностей. Возможно, помощь при принятии решения окажет знание того, как эти технологии используют в уже существующих проектах. Так, автор этого материала говорит, что они, в SessionStack, используют, в зависимости от ситуации, и WebSockets, и HTTP.
Когда библиотеку SessionStack интегрируют в веб-приложение, она начинает собирать и записывать все изменения DOM, события, возникающие при взаимодействии с пользователем, JS-исключения, результаты трассировки стека, неудачные сетевые запросы, отладочные сообщения, позволяя воспроизводить проблемные ситуации и наблюдать за всем, что происходит при работе пользователя с приложением. Всё это происходит в режиме реального времени и не должно влиять на производительность веб-приложения. Администратор может наблюдать за сеансом работы пользователя прямо в процессе работы этого пользователя. В этом сценарии в SessionStack решили использовать HTTP, так как двунаправленный обмен данными тут не нужен (сервер просто передаёт данные в браузер). Использование в подобной ситуации WebSocket было бы неоправданно, привело бы к усложнению поддержки и масштабирования решения. Однако, библиотека SessionStack, интегрируемая в веб-приложение, использует WebSocket, и, только если организовать обмен данными по WebSocket невозможно, переходит на HTTP.
Библиотека собирает данные в пакеты и отправляет на сервера SessionStack. В настоящее время реализуется лишь передача данных с клиента на сервер, но не наоборот, однако, некоторые возможности библиотеки, которые появятся в будущем, требуют двунаправленного обмена данными, именно поэтому здесь и используется технология WebSocket.
Уважаемые читатели! Пользовались ли вы технологиями WebSocket и HTTP/2+SSE? Если да — просим рассказать о том, какие задачи вы с их помощью решали, и о том, как вам понравилось то, что получилось.