Веб-приложения реального времени. Веб-сокеты, IIS 8, библиотека SignalR и их использование в приложениях ASP.NET. Часть первая, веб-сокеты.

Время очень быстро движется вперёд, вместе с ним и всё остальное не стоит на месте, в особенности всё, что касается мира информационных технологий. Не является исключением из всего этого и "всемирная паутина" или коротко – веб. В данном цикле статей я постараюсь рассказать о тредах современного мира веб-разработки, в частности будет описан протокол WebSocket и его использование в приложениях ASP.NET, а также библиотека SignalR и многое другое. В этой статье будет описан протокол WebSocket, вдаваться в самые дебри я не буду, но расскажу достаточно подробно об основных понятиях, чтобы можно было составить хорошее представление о веб-сокетах и их использовании в ASP.NET. Прежде чем начать немного исторической справки. Протокол HTTP был разработан в основном для обмена простой текстовой информацией между браузером пользователя и сервером. По мере развития веба потребности пользователей становились всё больше, увеличивался и размер пересылаемых данных. Веб-приложения состоящие из обычного текста и картинок, стали на порядок более сложными и визуально красивыми, вплотную приблизившись к дестопным аналогам. Параллельно развивались и технологии веб-разработки, а также сам протокол HTTP. Основным недостатком HTTP (у него ещё и много достоинств), является то, что он работает по принципу - "запрос - ответ", что неприемлимо для веб-приложений реального времени. Хотя есть несколько ухищрённых приёмов (костылей), которые помогают обойти это ограничение и будут описаны, когда речь пойдёт о библиотеке SignalR. Чтобы сделать веб-страницы интерактивными используется технология AJAX. Но веб-сокеты – ещё больший шаг в будущее, для реально "живых" веб-приложений, и предоставляющее ещё большее удобство, свободу и простоту. WebSocket - является веб-технологией, обеспечивающей полнодуплексные каналы связи поверх одного TCP соединения. Протокол WebSocket был стандартизирован инженерным советом интернета (IETF) как RFC 6455. При реализации, протокол WebSocket может быть использован в любых клиент-серверных приложениях. Следует отметить, что протокол WebSocket никак не связан с протоколом HTTP. Единственное, что протокол HTTP используется для проверки того, что стороны поддерживают протокол WebSocket. Если WebSockets обеими сторонами подтверждается, то весь последующий процесс обмена данными проходит без использования HTTP. Естественно, если одна из сторон (обычно таковыми являются веб-браузеры) запрашивает данную информацию, то это значит, что она уже поддерживает WebSocket. Для установки связи и перехода на протокол WebSocket клиент, в данном случае браузер, отправляет примерно такой запрос:

GET http://localhost/SimpleWebSocketApplication/WebSocketHandler.ashx HTTP/1.1
Origin: http://localhost
Sec-WebSocket-Key: x4s8Kx9DxOong3BWbw9hLw==
Connection: Upgrade
Upgrade: Websocket
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; Touch)
Host: localhost
DNT: 1
Cache-Control: no-cache
если сервер поддерживает веб-сокеты, то отвечает примерно так

HTTP/1.1 101 Switching Protocols
Cache-Control: private
Upgrade: Websocket
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
Sec-WebSocket-Accept: grjCf5Wq9QzX7E03KJQxU/wE8kw=
Connection: Upgrade
X-Powered-By: ASP.NET
Date: Sat, 08 Jun 2013 10:44:52 GMT
и дальше можно будет использовать веб-сокеты для обмена данными. Для просмотра подобных сведений можно использовать клиентский отладочный веб-прокси Fiddler.

Как видно из рисунка показанного выше, ответом на запрос HTTP GET является код состояния - 101, который указывает на переход на другой протокол. В даннном случае это WebSocket. Для протокола WebSocket были определены две новые URI схемы.

ws://имя хоста[:порт]/путь[?строка запроса]
wss://имя хоста[:порт]/путь[?строка запроса]
В случае с обычным соединением, данные не шифруются и запросы идут через TCP-порт 80. А при защищённом соединении, данные шифруются и передаются уже по порту 443. То есть примерно так же как и в HTTP. А как это всё можно использовать и что это с точки зрения веб-разработчика? Для использования данного протокола кнопки и формы, ссылки и прочее, которые используются в HTTP для отправки запросов и получения ответов уже не годятся. Для работы по протоколу WebSocket нужно создать JavaScript объект типа WebSocket и использовать его. Получается, что есть специальный API, который должен быть реализован браузером, для работы по данному протоколу с помощью языка JavaScript.

var webSocket = 
    new WebSocket(ws://localhost/SimpleWebSocketApplication/WebSocketHandler.ashx);
Открытие соединения и отправка запроса на переключение пртокола, которое было описано выше, происходит именно при создании и инициализации объекта WebSocket. Тип WebSocket поддерживает четыре основных метода обратного вызова, посредством которых и выполняется вся основная работа.

webSocket.onopen = function (e) { };

webSocket.onmessage = function (e) { };

webSocket.onclose = function (e) { };

webSocket.onerror = function (e) {};
Более подробно работа с этими методами будет описана в следующих статьях, а исчерпывающее описание WebSocket API можно посмотреть тут. Обмен данными между сторонами проходит посредством сообщений, которые состоят из одного или нескольких фреймов. Фрейм - это порция данных.

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

Рисунок более высокого качества можно скачать отсюда. Думаю, что посмотрев рисунок можно понять структуру фрейма, и у будет лишним описать всё это второй раз тут. Больше сведений на этот счёт можно получить отсюда. Но стоит дополнить вышепоказанное. Фреймы могут содержать двоичны данные в "сыром виде" или закодированные в кодировке UTF-8. Другие кодировки не поддерживаются, что очень хорошо. Теперь о соединении между сторонами.  После того, как стороны установят связь по протоколу WebSocket, оно остаётся открытым, пока одна из сторон не решает его закрыть. Для того, чтобы закрыть соединение, сторона-инициатор отправляет закрывающий фрейм (фрейм опкод которого равен 0x8). Ниже показан данный процесс.

Кроме обычного закрытия соединения бывает и аварийное, например потеря связи и т.п. Коды состояния закрытия соединения описаны тут. Ещё два типа управляющих фреймов, это фреймы для проверки состояния соединения с опкодами 0x9(ping) и 0xA(pong). Сторона проверяющая состояние соединения должна отправить фрейм первого типа, а отвечающая сторона должна ответить фреймом с теми данными, которые были посланы первой стороной, но уже с опкодом – pong. На запросы ping сервера отвечает сам браузер, из кода JavaScript эта возможность не доступна. Теперь корото о веб-браузерах поддерживающих данный протокол. Краткий список браузеров поддерживающих протокол WebSocket по последнему стандарту RFC 6455  приведён ниже:
  • Internet explorer 10 и выше
  • Mozilla Firefox 11 и выше
  • Google Chrome 16 и выше
  • Apple Safari 6.0
  • Opera 12.1

Выше показан только список  настольных браузеров, более полную информацию на этот счёт можно найти в сети, например тут. Следует также отметить, что черновые версии протокола поддерживались и в более ранних версиях некоторых браузеров. На этом описание основных особенностей протокола завершено. Напоследок омечу, что хотя и WebSocket достаточно новая технология, но она уже сегодня достаточно востребована и в будущем она будет востребована ещё больше. Хотя, думать, что она вытеснит HTTP глупо, по крайней мере в близжайшем будущем. В остальных статья будет описано использование данного протокола на практике, в приложениях ASP.NET.