На переднем крае

Исследуем потенциал WebSockets

Дино Эспозито

 

Дино ЭспозитоНынешняя «всемирная паутина» не рассчитана на то, чтобы быть несущей средой реального времени. Веб-приложения создают впечатление некоей непрерывности за счет традиционных решений на основе опросов, реализуемых через AJAX, или, возможно, с помощью запросов с длинными опросами (long-polling requests), в конечном счете реализуемых специальными библиотеками вроде SignalR и Comet. Для большинства приложений опрос (polling) — хорошее решение, даже если наблюдаются заметные задержки при взаимодействии клиента с сервером и сервера с клиентом. В этой статье мы исследуем новую альтернативу — WebSockets.

Нарастающая интеграция между Web и мобильными приложениями с социальными сетевыми сервисами (social media) понижает порог приемлемой задержки во взаимодействии «клиент-сервер». Обновляя свой статус в Facebook, вы хотите, чтобы эта информация немедленно становилась доступной вашим друзьям. Аналогично, когда кто-то выражает одобрение по одной из ваших заметок, вы хотите мгновенно получать уведомление об этом. Сегодня все эти возможности реальны, и это лишь одна из причин всемирного признания Facebook и взрывного развития социальных сетей. А это влечет за собой значительный спрос со стороны разработчиков на решения и инструментарий, позволяющие реализовать коммуникации через Web в реальном времени.

Чтобы добиться возможности соединения с нулевой задержкой между веб-клиентами и серверами, нужно выйти за рамки протокола HTTP. Именно для этого и создан WebSocket Protocol. Для этого протокола в настоящее время существует стандарт Internet Engineering Task Force; прочитать о нем можно по ссылке bit.ly/va6qSS. Стандартный API для реализации данного протокола в браузерах сейчас проходит этап официального оформления в консорциуме World Wide Web Consortium (W3C) (bit.ly/h1IsjB). Спецификация находится в состоянии «Candidate Recommendation».

WebSocket Protocol

Новый WebSocket Protocol призван преодолеть структурное ограничение протокола HTTP, которое делает его неэффективным для веб-приложений, которые выполняются в браузерах и которые должны оставаться подключенными к серверу по постоянному соединению. WebSocket Protocol обеспечивает двухстороннюю связь между веб-приложениями и веб-серверами через один TCP-сокет. Иначе говоря, этот протокол позволяет веб-приложению, выполняемому в браузере, оставаться подключенным к конечной точке в Web и в то же время сводит к минимуму нагрузку на сервер, в частности использование памяти и других его ресурсов. Конечный эффект заключается в том, что данные и уведомления могут передаваться между браузерами и веб-серверами без задержки, и больше нет нужды в дополнительных запросах. Может быть, это прозвучит слишком эмоционально, но WebSocket Protocol открывает разработчикам совершенно новый горизонт возможностей и оставляет трюки с опросами и соответствующие инфраструктуры в прошлом. Хотя… не совсем так.

Использование WebSockets сейчас

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

Это означает, что разработчики не могут просто взять и отказаться от кода, основанного на AJAX-опросах или решениях с поддержкой длинных опросов. Здесь будет уместно отметить, что в SignalR (будущей инфраструктуре Microsoft для обмена сообщениями между браузерами и веб-серверами с нулевой задержкой) проделана фантастическая работа по абстрагированию постоянного соединения, автоматическому переключению на WebSockets в тех случаях, когда это возможно, и использованию длинных запросов в остальных случаях. В SignalR есть все, чтобы она стала основной библиотекой и инструментом для каждого разработчика и любого веб-приложения.

Какие браузеры поддерживают WebSockets в данный момент?

В табл. 1 дана сводка по поддержке WebSockets со стороны наиболее популярных браузеров в настоящее время.

Табл. 1. Поддержка WebSockets браузерами

Браузер Поддержка WebSockets
Internet Explorer WebSockets будет поддерживаться в Internet Explorer 10. Приложения в стиле Metro, написанные с использованием JavaScript и HTML5, также будут поддерживать WebSockets
Firefox WebSockets поддерживается, начиная с версии 6 этого браузера, выпущенной в середине 2011 г. Кое-какая рудиментарная поддержка предлагалась в версии 4, но была удалена в версии 5
Chrome WebSockets поддерживается, начиная с версии 14, выпущенной в сентябре 2011 г.
Opera Поддержка WebSockets удалена в версии 11
Safari Поддерживает раннюю версию WebSocket Protocol

За исключением Firefox вы можете проверять поддержку WebSockets, просматривая объект window.WebSocket. В случае Firefox на данный момент нужно проверять объект MozWebSocket. Следует отметить, что поддержку большинства возможностей браузеров, относящихся к HTML5, можно проверять посредством специализированной библиотеки, такой как Modernizr (modernizr.com). В частности, вам потребуется написать следующий код на JavaScript, если вы связываете библиотеку Modernizr со своей страницей:

if (Modernizr.websockets)
{
  ...
}

На сегодняшний день Modernizr, по-видимому, является лучшим выбором, если вы хотите приступить к использованию реализации WebSocket, поскольку данная библиотека предоставляет поли-заполнения (polyfills) — код, автоматически запускаемый, когда нужная функциональность не поддерживается текущей версией браузера.

В конечном счете WebSockets — чрезвычайно полезная функциональность с весьма неоднородной на данный момент поддержкой среди различных поставщиков браузеров. Однако Microsoft ввела широкую поддержку WebSockets в предстоящую версию Internet Explorer 10, а также в IIS, ASP.NET, Windows Communication Foundation (WCF) и Windows Runtime (WinRT). Заметьте, что официального стандартного API пока нет, и поддержка на столь раннем этапе свидетельствует о большом интересе к этой функциональности. Лучшее, что вы можете сделать на сегодняшний день, — задействовать WebSockets через какой-либо уровень абстракции. Один из вариантов — Modernizr, если вы предпочитаете держаться поближе к «железу» и писать собственный код для открытия и закрытия WebSockets. SignalR — более интересный вариант, если вы ищете инфраструктуру, которая прозрачно связывает браузер и конечную точку в Web постоянным соединением безо всяких выкрутасов и без необходимости вникать в детали нижележащих механизмов.

Обзор WebSocket Protocol

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

WebSocket-взаимодействие начинается с процесса установления связи (handshake), в ходе которого две стороны (браузер и сервер) подтверждают свое намерение взаимодействовать по постоянному соединению. Далее по TCP в обе стороны посылается серия пакетов сообщений. На рис. 1 показана общая схема работы WebSocket Protocol.

Рис. 1. Схема WebSocket Protocol

Browser Браузер
Server Сервер
Request to Set up a WebSocket Connection Запрос на установление WebSocket-соединения
Response to Accept the Request Ответ о принятии запроса
Payload Полезные данные
Close Payload Закрывающие данные
Initial Handshake Процесс начального установления связи
Bidirectional Flow of Packets Двухсторонний поток пакетов
Closing the Connection Закрытие соединения

Заметьте, что в дополнение к показанному на рис. 1 при закрытии соединения обе конечные точки обмениваются кадром закрытия (close frame) для корректного завершения связи. В процессе начального установления связи клиент передает веб-серверу обычный HTTP-запрос. Этот запрос является командой HTTP GET, сконфигурированной как запрос переключения (upgrade request):

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com

В HTTP запрос клиента с заголовком Upgrade указывает на намерение клиента попросить сервер переключиться на другой протокол. В случае WebSocket Protocol запрос переключения к серверу содержит уникальный ключ, который сервер вернет искаженным как подтверждение того, что он принял запрос переключения. Это практическая демонстрация, показывающая, что сервер понимает WebSocket Protocol. Вот пример ответа на запрос установления связи:

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

Код состояния, равный 101, свидетельствует об успехе, а все остальные значения кода состояния будут интерпретироваться как отказ в переключении на WebSocket Protocol. Сервер сцепляет принятый ключ с фиксированной строкой GUID и вычисляет хеш по полученной строке. Затем хеш-значение кодируется по основанию Base64 и возвращается клиенту через заголовок Sec-WebSocket-Accept.

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

После установления связи клиент и сервер могут свободно обмениваться сообщениями по протоколу WebSocket Protocol. Полезные данные начинаются с операционного кода (opcode), который указывает выполняемую операцию. Один из таких кодов — 0x8 — сообщает о запросе на закрытие сеанса. Заметьте, что сообщения WebSocket передаются асинхронно, поэтому на отправленный запрос не обязательно будет получен мгновенный ответ — так же, как в HTTP. В случае WebSocket Protocol лучше мыслить в категориях универсальных сообщений, передаваемых от клиента серверу и наоборот, и забыть о традиционном шаблоне запроса/ответа в HTTP.

Типичный URL для конечной точки WebSocket принимает следующий вид:

var myWebSocket = new WebSocket("ws://www.websocket.org");

Префикс wss протокола используется, если вам нужно защищенное соединение через сокет (защищенные соединения, в целом, будут успешнее при наличии промежуточных точек). Наконец, WebSocket Protocol позволяет использовать коммуникации с перекрестными источниками (cross-origin communication). В принципе, клиенту WebSocket разрешается (не всегда) посылать запросы конечным точкам, расположенным в любом домене. Но решение о принятии или отклонении запроса на установление связи остается за сервером WebSocket.

Обзор WebSocket API

Как упоминалось, W3C в настоящее время занимается стандартизацией API для WebSocket Protocol, и браузеры постоянно догоняют различные черновые спецификации по мере их появления. Вы должны понимать, что любой код, работающий сегодня, может перестать работать в разных браузерах и даже, что важнее, в том же самом браузере, когда на рынке появится новый выпуск. Однако, как только у вас будет какой-то работающий WebSocket-код, вы более-менее готовы к любым изменениям, поскольку они скорее всего не будут глобальными.

Если вы хотите поэкспериментировать с WebSocket Protocol, зайдите на сайт websocket.org, используя браузер с поддержкой этого протокола. Например, вы можете воспользоваться предварительной версией Internet Explorer 10 или недавней версией Google Chrome. На рис. 2 показан процесс установления связи в том виде, в каком он отслеживается с помощью Fiddler.

Рис. 2. Реальное установление связи между браузером и сервером

Неудивительно, что текущая версия Fiddler (2.3.x) захватывает лишь HTTP-трафик. Однако новая версия Fiddler, умеющая анализировать WebSocket-трафик, в настоящее время находится на стадии беты.

WebSocket API довольно прост. На стороне браузера вам нужно создать экземпляр класса браузера WebSocket. Этот класс предоставляет набор интересных событий, для которых вы захотите создать соответствующие обработчики:

var wsUri = " ws://echo.websocket.org/";
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onerror = function(evt) { onError(evt) };

Событие onopen срабатывает, когда соединение установлено, событие onmessage — когда клиент принимает сообщение от сервера, событие onclose — когда соединение закрыто, а событие onerror — когда возникает любая ошибка.

Чтобы отправить сообщение серверу, вам нужно лишь поместить вызов метода send:

var message = "Cutting Edge test: " +  new Date().toString();
websocket.send(message);

На рис. 3 показана страница, которая является адаптированной версией примера Echo Test с веб-сайта websocket.org. В этом примере сервер просто отвечает тем же сообщением, которое он принял от клиента.

Рис. 3. WebSocket Protocol в действии

Если вы заинтересованы в программировании с использованием WebSocket для Internet Explorer 10, см. ссылку bit.ly/GNYWFh.

Серверная сторона WebSockets

В этой статье основное внимание было уделено клиентской стороне WebSocket Protocol. Но очевидно, что для использования клиента WebSocket необходим WebSocket-совместимый сервер, который понимает запросы и может соответственно отвечать на них. В последнее время стали появляться инфраструктуры для создания WebSocket-серверов. Например, вы можете опробовать Socket.IO для Java и Node.js (socket.io). Если вы ищете что-то под Microsoft .NET Framework, изучите «Web Socket Server» от The Code Project на bit.ly/lc0rjt. Кроме того, серверная поддержка Microsoft для WebSockets доступна в IIS, ASP.NET и WCF. Детали см. в видеоролике «Building Real-Time Web Apps with WebSockets Using IIS, ASP.NET and WCF» на Channel 9 (bit.ly/rnYaw5).

Заключение

По мнению многих, WebSockets — едва ли не самое полезное изобретение после горячей воды. Когда вы ухватите суть WebSockets, вы будете удивляться, как это мир программного обеспечения мог обходиться без этих сокетов. WebSockets здорово пригодится в ряде приложений, но отнюдь не во всех. В любом приложении, где мгновенный обмен сообщениями является ключевым требованием, — имеет смысл серьезно подумать о создании WebSocket-сервера и клиентов (веб-приложения, мобильные и даже настольные программы). Еще одна группа приложений, которые получат выигрыш от внедрения WebSocket Protocol, — игры и каналы реального времени. Да, WebSockets — определенно лучшее, что было изобретено после горячей воды!


Дино Эспозито (Dino Esposito) — автор книг «Programming ASP.NET MVC 4» (Microsoft Press, 2011) и «Programming ASP.NET MVC 3» (Microsoft Press, 2010), соавтор «Microsoft .NET: Architecting Applications for the Enterprise» (Microsoft Press, 2008). Проживает в Италии и часто выступает на отраслевых мероприятиях по всему миру. Читайте его заметки на twitter.com/despos.

Выражаю благодарность за рецензирование статьи экспертам Леви Бродерику (Levi Broderick) и Брайену Реймору (Brian Raymor).