Что такое websocket и как их использовать
Вебсокеты: боевое применение
Вебсокеты — это прогрессивный стандарт полнодуплексной (двусторонней) связи с сервером по 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% |
Как видно, вполне готово к употреблению.
Тут могла бы быть заключительная резюмирующая часть с итогами, библиографией и моралью. Но я сэкономлю ваше время и скажу просто: вебсокеты — это очень круто!
Как использовать Websocket на примере простого Express API?
Краткое описание технологии
Websocket — это протокол связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени.
Для установления соединения WebSocket клиент и сервер используют протокол, похожий на HTTP. Клиент формирует особый HTTP-запрос, на который сервер отвечает определенным образом.
Несмотря на «похожесть» новых запросов и ответов на запросы и ответы протокола HTTP, они таковыми не являются. Например, в запросе есть тело, но в заголовках поле «Content-Length» отсутствует (что нарушает соглашения HTTP). Подробнее об этом можно прочитать в Википедии.
Одним из главных преимуществ технологии — это ее простота. На клиенте и сервере есть всего 4 события для обработки:
Почему Websocket?
Кроме ws существуют еще два способа непрерывной передачи данных: Server-Sent Events (SSE) и Long Polling.
Приведем сравнения механизмов непрерывной связи сервера и клиента, а также сделаем выводы, почему стоит (или не стоит) использовать вебсокет.
Websocket | sse | long pooling | |
---|---|---|---|
протокол | websocket (ws, или wss) | HTTP(S) | HTTP(S) |
скорость | высокая | низкая | низкая |
направленность потоков данных | двунаправленная | однонаправленная | двунаправленная |
дополнительно | передача бинарных данных, отсутствует поддержка некоторых старых браузеров | автоматическое переподключение при обрыве соединения |
Одним из главных преимуществ технологии ws — это скорость передачи данных. SSE и LP используют протокол HTTP(S) и работают примерно так:
Пример работы простейшего api.
Что здесь происходит?
Чтобы создать сервер поддерживающий ws, мы создаем обычный http сервер, а потом привязываем к нему при создании websocket сервер.
Функция “on” помогает управлять событиями websocket. Самым примечательным является событие message, так что рассмотрим его подробнее.
Здесь функция получает параметр m — сообщение, то есть то, что отправил пользователь. Таким образом мы можем отправить с клиента строку и обработать ее на сервере. В данном случае сервер просто пересылает это сообщение всем, кто подключен к серверу websocket. Массив clients объекта webSocketServer содержит все подключения к серверу. Объект ws в то же время хранит данные только об одном подключении.
Не стоит использовать такой подход в реальном приложении. Если описать api таким образом, то сервер не может отличить один запрос от другого. О том, как можно построить api на основе websocket будет написано далее.
Взаимодействие с сервером на клиенте будет выглядеть так:
API на основе Websocket
В отличие от REST API, где запросы распределены по разным url, Websocket API имеет только один url. Для того, чтобы построить полноценное API на основе вебсокетов, необходимо научить систему отличать один запрос от другого. Это можно реализовать следующим образом:
1) С клиента мы будем передавать запросы в виде строки-json, которую распарсим на сервере:
2) На сервере мы распарсим строку и выделем в ней поле event — тип запроса. Пропишем для каждого типа соответствующий ответ:
Таким образом мы можем отправлять разные запросы на сервер и обрабатывать ответ в зависимости от запроса.
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 повсеместно.
Осторожно, двери закрываются. Не опоздайте на поезд в будущее.
WebSocket: что это, когда следует использовать и какие преимущества дает
Существуют разные способы передачи данных от браузера или приложения к серверам и обратно.
Правила этих способов описаны в специальных протоколах. Некоторые применяют там, где нет необходимости обмениваться данными быстро, например на информационных сайтах, другие там, где важна скорость, в частности в интернете вещей.
Мы совместно с экспертами из компании Git in Sky решили рассказать об одной из таких технологий — протоколе передачи данных веб-сокет (WebSocket) — и объяснить принцип его работы и преимущества на простых примерах.
Чтобы понять, что такое протокол WebSocket, давайте посмотрим, каким был интернет до него
Раньше, чтобы получить новую информацию от сервера, клиент (браузер) должен был направить ему запрос, а сервер отправлял ответ. Без запроса не было ответа, то есть обновления страницы — сервер не мог ничего отправить сам. Например, пользователь получил сообщение или push-уведомление на сайте. Чтобы клиент об этом узнал, он должен опрашивать сервер с некоторой периодичностью, нет ли новых данных.
Другой вариант — клиент узнавал о новых данных только тогда, когда пользователь взаимодействовал с сайтом: переходил на другие страницы, вручную обновлял вкладку или открывал сайт заново. Как правило, использовался первый метод: клиент, к примеру, раз в 5 секунд отправлял запрос на сервер; если было что-то новое, сервер отдавал эту информацию в ответ.
В таком подходе были недостатки:
Веб-сокеты же позволяют устанавливать постоянное соединение, и теперь сервер может сам отправить клиенту новые данные, не дожидаясь запроса. Эта интерактивность устраняет вышеперечисленные недостатки. Чтобы увидеть преимущества протокола WebSocket, посмотрим, как он работает, сравнив его с протоколом HTTP.
Как работает WebSocket и в чем его отличия от HTTP
Протокол HTTP однонаправленный. После цикла «запрос — ответ» соединение закрывается, а любой следующий запрос каждый раз устанавливает новое соединение с сервером: сколько запросов, столько и соединений. Процесс передачи данных происходит с некоторыми задержками за счет того, что есть накладные расходы на установку нового соединения при каждом запросе/ответе, а также сетевая и серверная нагрузка из-за обилия периодических запросов. В чистом виде протокол HTTP сейчас используется все реже, ему на смену приходит HTTPS. Это не отдельный протокол, а надстройка над HTTP, позволяющая шифровать данные.
Протокол WebSocket двунаправленный, полнодуплексный, что означает, что он может одновременно и получать, и передавать информацию. Веб-сокет делает это множество раз в одном открытом соединении. У такого соединения и скорость выше, чем у HTTP.
У веб-сокетов также есть возможность шифровать передаваемые данные, для этого используется надстройка над протоколом — WSS. Если передаваемые данные не зашифрованы, они становятся объектом для привлечения таких угроз, как несанкционированный доступ к клиенту третьих сторон, использование вредоносного ПО. Специальные надстройки протоколов передачи данных кодируют информацию на стороне отправителя и раскодируют на стороне получателя, оставляя ее зашифрованной для любых посредников. Так достигается безопасный транспортный уровень.
Для наглядности покажем, в чем различия технологий HTTP и WebSocket, на примере их работы в чатах.
Чтобы пользователи могли получать новые сообщения, браузер периодически опрашивал сервер: есть ли новые сообщения для пользователя? Каждый такой запрос устанавливал новое соединение и создавал лишнюю сетевую нагрузку.
Схема обмена сообщениями через HTTP-протокол
WebSocket: особенности протокола и пример использования на React
Руководитель группы разработки digital-интегратора DD Planet
Интернет — основа сети, техническая инфраструктура, благодаря которой и существует World Wide Web, или Всемирная паутина. По своей сути интернет — гигантская сеть компьютеров, которые могут взаимодействовать друг с другом.
Принципы взаимодействия и способы передачи данных между этими компьютерами определяются сетевыми протоколами. Иными словами, сетевой протокол — это набор правил и действий, который регулирует соединение и обмен данными между двумя и более включенными в сеть устройствами.
В этой статье я расскажу про популярный в наше время протокол — WebSocket. Он используется, как правило, при разработке приложений, в которых содержимое обновляется с высокой частотой или в реальном времени.
Описание
WebSocket обеспечивает обмен данными между браузером и сервером через постоянное соединение. Это двунаправленный, полнодуплексный протокол, который используется в сценарии взаимодействия «Клиент-сервер». Он начинается с ws:// или wss:// в случае безопасного соединения.
Принцип веб-сокета — соединение между клиентом и сервером остается активным до тех пор, пока оно не будет разорвано любой из сторон.
Соединение WebSocket устанавливается посредством HTTP-запроса:
Особенности протокола
О плюсах WebSocket:
Где применяется WebSocket
WebSocket может быть очень полезен для повышения скорости работы сайта. Но не стоит использовать этот протокол в случаях, когда мы хотим получать старые или неизменные данные, или необходимо загрузить данные лишь один раз. В таких кейсах стоит применить протокол HTTP.
Где используется WebSocket:
WebSocket подходит для этих проектов лучше всего, так как в них клиент может не выполнять на своей стороне никаких вычислений, а лишь получать\передавать данные на сервер.
Легкость протокола позволяет с высокой частотой отправлять или получать информацию. Например, моментально отображать обновления в онлайн-игре, когда соперник выполнил определенные действия, или загружать данные с наименьшей задержкой на трейдерской платформе, что будет положительно сказываться на результате торгов.
Пример из практики
В качестве примера покажу React-component, работающий с веб-сокетом. Его суть проста — отображать текущее состояние соединения и функциональность принудительного закрытия и открытия соединения.
Сервер веб-сокета — ресурс wss://ws.kraken.com/, который с определённой периодичностью отправляет клиентам сообщение о том, что сервер работает.
Использование протокола WebSocket в React-приложении выглядит так:
Протокол WebSocket позволяет быстро и безопасно пересылать данные на любой домен, помогает увеличить скорость сайта или приложения. Он поддерживается всеми популярными браузерами: Google Chrome, Apple Safari, Mozilla Firefox, Opera и Internet Explorer, что делает его универсальным.