Объекты WebSocket в Windows Consumer Preview

Internet Explorer 10 и все другие клиентские и серверные компоненты Microsoft WebSocket в Windows 8 Consumer Preview и Server Beta теперь поддерживают конечную версию протокола IETF WebSocket. Кроме того, в Internet Explorer 10 реализован интерфейс WebSocket API, получивший статус кандидата к рекомендации (Candidate Recommendation) консорциума W3C.

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

В своей предыдущей записи блога я описал сценарии WebSocket:

Объекты WebSocket позволяют веб-приложениям доставлять в браузер оповещения и обновления в реальном времени. Стремясь обойти ограничения в исходной модели HTTP "запрос-ответ" для браузера, разработчики столкнулись с проблемами, поскольку эта модель не была предназначена для сценариев работы в реальном времени. Объекты WebSocket позволяют браузерам открывать двунаправленные полнодуплексные каналы связи со службами. Каждая сторона может использовать такой канал для немедленной отправки данных другой стороне. Сегодня многие сайты — от социальных сетей и игр до финансовых сайтов — могут лучше обеспечивать сценарии в реальном времени, идеально используя одинаковую разметку в различных браузерах.

Со времени записи блога, опубликованной в сентябре 2011 года, рабочие группы добились значительного прогресса. Протокол WebSocket теперь является предложенным стандартом IETF. Кроме того, интерфейс W3C WebSocket API получил статус кандидата к рекомендации (Candidate Recommendation) консорциума W3C.

Пример интерфейса WebSocket API с использованием эха

В приведенных ниже фрагментах кода используется простой эхо-сервер, созданный с пространством имен System.Web.WebSockets для ASP.NET, чтобы возвращать текстовые и двоичные сообщения, отправленные из приложения. Это приложение позволяет пользователю вводить текст, который будет отправлен и возвращен обратно в виде текстового сообщения, или создавать рисунок, который может быть отправлен и возвращен как двоичное сообщение.

Снимок экрана с примером приложения с эхом WebSocket.

С более сложным примером, позволяющим экспериментировать и определять разницу в задержке и быстродействии при использовании протоколов WebSocket и HTTP, можно ознакомиться в демонстрации Flipbook.

Подключение к серверу WebSocket

Это простое объяснение основано на прямом подключении между приложением и сервером. Если настроен прокси-сервер, Internet Explorer 10 начинает процесс, отправляя запрос HTTP CONNECT прокси-серверу.

Когда объект WebSocket создан, клиент и сервер обмениваются подтверждениями установления связи, чтобы установить подключение WebSocket.

Схема, иллюстрирующая запрос на обновление HTTP GET от HTTP-клиента HTTP-серверу.

Internet Explorer 10 начинает процесс, отправляя HTTP-запрос серверу:

GET /echo HTTP/1.1

Host: example.microsoft.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Origin: http://microsoft.com

Sec-WebSocket-Version: 13

Давайте рассмотрим каждую часть этого запроса.

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

GET /echo HTTP/1.1

Host: example.microsoft.com

HTTP-заголовок Upgrade запрашивает у сервера переключение протокола уровня приложения с HTTP на WebSocket.

Upgrade: websocket

Connection: Upgrade

Сервер преобразует значение в заголовке Sec-WebSocket-Key в ответе, чтобы показать, что он понимает протокол WebSocket:

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Заголовок Origin задается браузером Internet Explorer 10, чтобы разрешить серверу усилить безопасность на основе механизма Origin.

Origin: http://microsoft.com

Заголовок Sec-WebSocket-Version определяет запрошенную версию протокола. Версия 13 является окончательной версией в предложенном стандарте IETF:

Sec-WebSocket-Version: 13

Если сервер принимает запрос на обновление протокола уровня приложения, он возвращает ответ HTTP 101 Switching Protocols (Переключение протоколов):

Схема, иллюстрирующая ответ HTTP 101 Switching Protocols от клиента HTTP-сервера HTTP-клиенту.

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Чтобы показать, что он понимает протокол WebSocket, сервер выполняет стандартное преобразование в заголовке Sec-WebSocket-Key из запроса клиента и возвращает результаты в заголовке Sec-WebSocket-Accept:

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Затем Internet Explorer 10 сравнивает заголовок Sec-WebSocket-Key с Sec-WebSocket-Accept, чтобы проверить, что сервер является сервером WebSocket, а не HTTP-сервером "с манией величия".

Подтверждение клиента устанавливает подключение HTTP-на-TCP между Internet Explorer 10 и сервером. После того как сервер возвращает ответ 101, протокол на уровне приложения переключается с HTTP на протокол WebSocket, использующий ранее установленное подключение TCP.

С этого момента протокол HTTP совсем не нужен. Используя облегченный протокол связи WebSocket, любая конечная точка теперь может отправлять и получать сообщения в любое время.

Схема, иллюстрирующая отправку сообщений WebSocket между двумя веб-сокетами.

Программирование подключения к серверу WebSocket

Протокол WebSocket определяет две новые схемы URI, похожие на схемы HTTP.

  • "ws:" "//" хост [ ":" порт ] путь [ "?" запрос ] моделируется по схеме "http:". Для этой схемы по умолчанию используется порт 80. Он используется для незащищенных (незашифрованных) подключений.
  • "wss:" "//" хост [ ":" порт ] путь [ "?" запрос ] моделируется по схеме "https:". Для этой схемы по умолчанию используется порт 443. Он используется для безопасных подключений, осуществляемых через протокол TLS (Transport Layer Security).

При наличии прокси-серверов или сетевых промежуточных средств вероятность успешной установки безопасных подключений выше, поскольку промежуточные средства реже предпринимают попытки преобразовать безопасный трафик.

Следующий фрагмент кода устанавливает подключение WebSocket:

var host = "ws://example.microsoft.com";

var socket = new WebSocket(host);

ReadyState — на старт … внимание … марш …

Атрибут WebSocket.readyState представляет состояние подключения: CONNECTING (Подключается), OPEN (Открыто), CLOSING (Закрывается) или CLOSED (Закрыто). Когда WebSocket создается впервые, для атрибута readyState задается значение CONNECTING (Подключается). Когда подключение установлено, для атрибута readyState задается значение OPEN (Открыто). Если не удается установить подключение, для атрибута readyState задается значение CLOSED (Закрыто).

Регистрация для событий Open

Для получения оповещений после создания подключения приложение должно быть зарегистрировано для событий Open.

socket.onopen = function (openEvent) {

document.getElementById("serverStatus").innerHTML = 'Web Socket State::' + 'OPEN';

};

Механизм отправки и получения сообщений

После успешного подтверждения установления связи приложение и сервер Websocket могут обмениваться сообщениями WebSocket. Сообщение составляется в виде последовательности, состоящей из одного или нескольких фрагментов сообщения или "кадров" данных.

В каждом кадре содержится следующая информация:

  • Длина кадра
  • Тип сообщения (двоичное или текстовое) в первом кадре сообщения
  • Флаг (FIN), указывающий, является ли этот кадр последним в сообщении

Схема, иллюстрирующая содержимое кадров WebSocket.

Internet Explorer 10 заново собирает кадры в целое сообщение перед передачей его в скрипт.

Программирование отправки и получения сообщений

Благодаря send API приложения могут отправлять сообщения на сервер Websocket в виде текста в кодировке UTF-8, объектов ArrayBuffer или больших двоичных объектов (Blob-объектов).

Например, этот фрагмент кода извлекает текст, введенный пользователем, и отправляет его на сервер в виде текстового сообщения в кодировке UTF-8, чтобы оно вернулось назад. Он проверяет, что атрибут readyState объекта WebSocket имеет значение OPEN (Открыто):

function sendTextMessage() {

if (socket.readyState != WebSocket.OPEN)

return;

 

var e = document.getElementById("textmessage");

socket.send(e.value);

}

Этот фрагмент кода извлекает изображение, созданное пользователем на полотне, и отправляет его на сервер в виде двоичного сообщения:

function sendBinaryMessage() {

if (socket.readyState != WebSocket.OPEN)

return;

 

var sourceCanvas = document.getElementById('source');

// msToBlob returns a blob object from a canvas image or drawing

socket.send(sourceCanvas.msToBlob());

// ...

}

Регистрация для событий Message

Для получения сообщений приложение должно быть зарегистрировано для событий Message. Обработчик событий получает событие MessageEvent, содержащее данные в MessageEvent.data. Данные могут быть получены в виде текстовых или двоичных сообщений.

При получении двоичного сообщения атрибут WebSocket.binaryType контролирует, возвращаются ли данные сообщения как данные с типом Blob-объект или ArrayBuffer. Для этого атрибута может быть задано значение либо "blob", либо "arraybuffer".

В приведенном ниже примере используется значение по умолчанию — "blob".

Этот фрагмент кода получает возвращенное изображение или возвращенный текст от сервера WebSocket. Если данные представляют собой Blob-объект, то было возвращено изображение, и оно создается на конечном полотне. В противном случае было возвращено текстовое сообщение в кодировке UTF-8, и оно отображается в текстовом поле.

socket.onmessage = function (messageEvent) {

if (messageEvent.data instanceof Blob) {

var destinationCanvas = document.getElementById('destination');

var destinationContext = destinationCanvas.getContext('2d');

var image = new Image();

image.onload = function () {

destinationContext.clearRect(0, 0, destinationCanvas.width, destinationCanvas.height);

destinationContext.drawImage(image, 0, 0);

}

image.src = URL.createObjectURL(messageEvent.data);

} else {

document.getElementById("textresponse").value = messageEvent.data;

}

};

Закрытие подключения WebSocket

Помимо подтверждения открытия подключения, есть и подтверждение закрытия. Любая конечная точка (приложение или сервер) может инициировать это подтверждение.

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

Когда другая конечная точка получает кадр закрытия, она отправляет в ответ собственный кадр закрытия. Прежде чем ответить кадром закрытия, она может отправить ожидающие сообщения.

Схема, иллюстрирующая сообщение с кадром закрытия и ответ.

Программирование закрытия WebSocket и регистрация для событий Close

Приложение инициирует подтверждение закрытия открытого подключения с помощью close API:

socket.close(1000, "normal close");

Для получения оповещений после закрытия подключения приложение должно быть зарегистрировано для событий Close.

socket.onclose = function (closeEvent) {

document.getElementById("serverStatus").innerHTML = 'Web Socket State::' + 'CLOSED';

};

close API принимает два необязательных параметра: код состояния, определяемый протоколом, и описание. Код состояния должен быть либо 1000, либо в диапазоне от 3000 до 4999. При выполнении закрытия атрибут readyState получает значение CLOSING (Закрывается). После того как Internet Explorer 10 получает от сервера ответ Close, для атрибута readyState задается значение CLOSED (Закрыто) и выполняется событие Close.

Использование Fiddler для просмотра трафика WebSocket

Fiddler — это известный прокси-сервер для отладки HTTP. В его новейших версиях предлагается некоторая поддержка протокола WebSocket. Вы можете проверять заголовки, используемые в подтверждениях WebSocket:

Снимок экрана Fiddler с запросом WebSocket.

Кроме того, все сообщения WebSocket записываются в журнал. На приведенном ниже снимке экрана видно, что слово "spiral" (спираль) было отправлено на сервер в виде текстового сообщения в кодировке UTF-8 и возвращено обратно:

Снимок экрана Fiddler с ответом WebSocket.

Заключение

Если вы хотите больше узнать об объектах WebSocket, можете посмотреть следующие сеансы с конференции Microsoft //Build/, проходившей в сентябре 2011 года:

Если вас интересует использование технологий Майкрософт для создания службы WebSocket, ознакомьтесь со следующими записями:

Я рекомендую вам начать разработку с использованием WebSocket уже сегодня и поделиться с нами своими впечатлениями.

— Брайан Реймор (Brian Raymor), старший руководитель программы, сетевые возможности Windows