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

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

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

Технологии, которые позволяют серверу отправлять данные клиенту в тот самый момент, когда он узнает о доступности новых данных, существуют уже довольно давно. Они носят такие имена, как «Пуш» или «Комета» . Один из наиболее распространенных приемов создания иллюзии соединения, инициированного сервером, называется длинным опросом. При длительном опросе клиент открывает 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 . В настоящее время по состоянию на февраль 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 — расширения. Используя расширения, можно будет отправлять кадры сжатыми , мультиплексированными и т. д. Вы можете найти расширения, принятые сервером, проверив свойство расширений объекта 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 недоступен. Библиотека, которая стала очень популярной в этой области, — этоocket.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 всякий раз, когда вам нужно соединение между клиентом и сервером с действительно низкой задержкой и практически в реальном времени. Имейте в виду, что это может потребовать переосмысления того, как вы создаете серверные приложения, с новым акцентом на таких технологиях, как очереди событий. Некоторые примеры использования:

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

Демо

Рекомендации