Знакомство с WebSockets — перенос сокетов в Интернет

Проблема: низкая задержка клиент-серверных и сервер-клиентских соединений.

Веб в значительной степени был построен вокруг так называемой парадигмы запроса/ответа HTTP. Клиент загружает веб-страницу, и затем ничего не происходит, пока пользователь не нажмет на следующую страницу. Около 2005 года AJAX начал делать веб более динамичным. Тем не менее, все HTTP-коммуникации управлялись клиентом, который требовал взаимодействия с пользователем или периодических опросов для загрузки новых данных с сервера.

Технологии, которые позволяют серверу отправлять данные клиенту в тот самый момент, когда он узнает о доступности новых данных, существуют уже довольно давно. Они называются «Push» или «Comet» . Один из самых распространенных приемов создания иллюзии инициированного сервером соединения называется «длинный опрос». При «длинном опросе» клиент открывает HTTP-соединение с сервером, которое сохраняет его открытым до отправки ответа. Всякий раз, когда у сервера действительно появляются новые данные, он отправляет ответ (другие методы включают Flash , многокомпонентные запросы XHR и так называемые htmlfiles ). «Длинный опрос» и другие методы работают довольно хорошо. Вы используете их каждый день в таких приложениях, как чат GMail.

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

Знакомство с WebSocket: внедрение сокетов в Интернет

Спецификация WebSocket определяет API, устанавливающий «сокетные» соединения между веб-браузером и сервером. Проще говоря: существует постоянное соединение между клиентом и сервером, и обе стороны могут начать отправлять данные в любое время.

Начиная

Вы открываете соединение WebSocket, просто вызывая конструктор WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Обратите внимание на ws: . Это новая схема URL для соединений WebSocket. Также есть wss: для безопасного соединения WebSocket, так же как https: используется для безопасных соединений HTTP.

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

Второй аргумент принимает необязательные подпротоколы. Это может быть строка или массив строк. Каждая строка должна представлять имя подпротокола, и сервер принимает только один из переданных подпротоколов в массиве. Принятый подпротокол можно определить, обратившись к свойству protocol объекта WebSocket.

Имена подпротокола должны быть одними из зарегистрированных имен подпротокола в реестре IANA . В настоящее время зарегистрировано только одно имя подпротокола (soap) по состоянию на февраль 2012 года.

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

Связь с сервером

Как только у нас появится соединение с сервером (когда срабатывает событие open ), мы можем начать отправлять данные на сервер, используя метод send('your message') на объекте соединения. Раньше он поддерживал только строки, но в последней спецификации теперь он может отправлять и двоичные сообщения. Для отправки двоичных данных можно использовать объект Blob или ArrayBuffer .

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

Аналогично сервер может отправлять нам сообщения в любое время. Всякий раз, когда это происходит, срабатывает обратный вызов onmessage . Обратный вызов получает объект события, а фактическое сообщение доступно через свойство data .

WebSocket также может получать двоичные сообщения в последней спецификации. Двоичные кадры могут быть получены в формате Blob или ArrayBuffer . Чтобы указать формат полученного двоичного файла, установите свойство binaryType объекта WebSocket на 'blob' или 'arraybuffer'. Формат по умолчанию — 'blob'. (Вам не нужно выравнивать параметр binaryType при отправке.)

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

Еще одна новая функция WebSocket — расширения. Используя расширения, можно будет отправлять кадры сжатыми , мультиплексированными и т. д. Вы можете найти принятые сервером расширения, изучив свойство extensions объекта WebSocket после события open. По состоянию на февраль 2012 г. официально опубликованной спецификации расширений пока нет.

// Determining accepted extensions
console.log(connection.extensions);

Кросс-источниковая коммуникация

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

Прокси-серверы

Каждая новая технология приносит с собой новый набор проблем. В случае WebSocket это совместимость с прокси-серверами, которые являются посредниками HTTP-соединений в большинстве корпоративных сетей. Протокол WebSocket использует систему обновления HTTP (которая обычно используется для HTTP/SSL) для «обновления» HTTP-соединения до соединения WebSocket. Некоторым прокси-серверам это не нравится, и они разрывают соединение. Таким образом, даже если данный клиент использует протокол WebSocket, может оказаться невозможным установить соединение. Это делает следующий раздел еще более важным :)

Используйте WebSockets сегодня

WebSocket — это пока еще молодая технология, не полностью реализованная во всех браузерах. Однако вы можете использовать WebSocket сегодня с библиотеками, которые используют один из упомянутых выше резервных вариантов, когда WebSocket недоступен. Библиотека, которая стала очень популярной в этой области, — socket.io , которая поставляется с клиентской и серверной реализацией протокола и включает резервные варианты (socket.io пока не поддерживает двоичные сообщения по состоянию на февраль 2012 года). Существуют также коммерческие решения, такие как PusherApp , которые можно легко интегрировать в любую веб-среду, предоставляя HTTP API для отправки сообщений WebSocket клиентам. Из-за дополнительного HTTP-запроса всегда будут дополнительные накладные расходы по сравнению с чистым WebSocket.

Серверная часть

Использование WebSocket создает совершенно новый шаблон использования для серверных приложений. Хотя традиционные серверные стеки, такие как LAMP, разработаны вокруг цикла HTTP-запрос/ответ, они часто не справляются с большим количеством открытых соединений WebSocket. Для поддержания большого количества открытых соединений одновременно требуется архитектура, которая получает высокую степень параллелизма при низких затратах на производительность. Такие архитектуры обычно разрабатываются вокруг потоковой обработки или так называемого неблокируемого ввода-вывода.

Реализации на стороне сервера

Версии протокола

Протокол связи (рукопожатие и передача данных между клиентом и сервером) для WebSocket теперь — RFC6455 . Последние версии Chrome и Chrome для Android полностью совместимы с RFC6455, включая двоичные сообщения. Кроме того, Firefox будет совместим с версией 11, Internet Explorer — с версией 10. Вы по-прежнему можете использовать более старые версии протокола, но это не рекомендуется, поскольку они известны своей уязвимостью. Если у вас есть серверные реализации для более старых версий протокола WebSocket, мы рекомендуем вам обновить его до последней версии.

Варианты использования

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

  • Многопользовательские онлайн-игры
  • Чат-приложения
  • Спортивные трансляции в прямом эфире
  • Обновление социальных лент в режиме реального времени

Демо

Ссылки