Опубликован: 15.05.2013 | Доступ: свободный | Студентов: 265 / 9 | Длительность: 24:25:00
Специальности: Системный архитектор
Лекция 9:

Синдикация

< Лекция 8 || Лекция 9: 1234 || Лекция 10 >

Веб-сокеты: MessageWebSocket и StreamWebSocket

Рассмотрев сокеты датаграмм и потоков в действии, мы можем посмотреть на их эквиваленты со стороны WebSocket. Как вы уже, возможно, знаете WebSockets это стандарт, созданный для использования HTTP (и, таким образом, TCP) для установки первоначального соединения, после чего обмен данными происходит через сокеты посредством TCP. Такой подход обеспечивает простоту использования HTTP-запросов на первых шагах взаимодействия и последующую эффективность обмена данными, реализуемую сокетами.

Как и в случае с обычными сокетами, веб-сокеты в WinRT поддерживают и передачу отдельных пакетов данных, и потоковую передачу: класс MessageWebSocket (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.messagewebsocket.aspx) предназначен для передачи отдельных пакетов, как и в случае с сокетами датаграмм (однако, он использует TCP, а не UDP), и StreamWebSocket (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.streamwebsocket.aspx) предоставляющий средства потоковой передачи данных через сокеты. Оба класса очень похожи на соответствующие им классы DatagramSocket и StreamSocket, настолько, что даже их интерфейсы во многом совпадают (с различными дополнительными типами наподобие MessageWebSocketControl):

  • Как и у DatagramSocket, у MessageWebSocket есть свойства control, information, и outputStream, событие messagereceived, и методы connectAsync и close. Здесь, кроме того, есть событие closed и метод setRequestHeader.
  • Как и у StreamSocket, у StreamWebSocket есть свойства control, information, inputStream, и outputStream, и методы connectAsync и close. Здесь, кроме того, есть событие closed и метод setRequestHeader.

Вы можете заметить, что здесь нет эквивалента StreamSocketListener. Это потому что процесс установки такого соединения обрабатывается с помощью HTTP-запросов, таким образом выделенный прослушиватель не нужен. Кроме того, поэтому в вышеперечисленных классах есть методы setRequestHeader: с их помощью можно настраивать HTTP-запросы. В том же духе, вы можете обнаружить, что методы connectAsync принимают Windows.Foundation.Uri а не имена узлов и служб. Но в основном мы видим то же самое поведение после установления соединения, с потоками и объектами DataReader и DataWriter.

Врезка: Сравнение API W3C и WinRT APIs для WebSockets

Стандартные веб-сокеты, в том виде, в котором они определены в API W3C, полностью поддерживаются приложениями для Магазина Windows. Однако, они поддерживают лишь UDP-модель, основанную на операциях обмена данными, наподобие DatagramSocket и только текстовое содержимое. MessageWebSocket в WinRT поддерживает и текст и двоичные данные, в дополнение к этому, вы можете использовать StreamWebSocket для реализации потоковой передачи данных с использованием TCP. API WinRT так же возвращают более подробные сведения об ошибках и обычно их использование предпочтительнее API W3C.

Рассмотрим всё это в контексте примера "Соединение с WebSocket" ( http://code.msdn.microsoft.com/windowsapps/Connecting-with-WebSockets-643b10ab). Этот пример зависит от серверной страницы ASP.NET, исполняющейся на локальном хосте, поэтому сначала вам нужно пройти в папку примера Server и запустить powershell.exe -ExecutionPolicy unrestricted file setupserver.ps1 из командной строки администратора. (Больше о настройке Internet Information Services и локального хоста вы можете узнать из Главы 2.). Если скрипт выполнен успешно, вы увидите папку WebSocketSample folder в c:\inetpub\wwwroot , она содержит файл EchoWebService.ashx. Кроме того, как указано в Главе 2, вы можете запустить веб-инсталлятор (http://www.microsoft.com/web/downloads/platform.aspx) для того, чтобы установить Visual Studio 2012 Express для Web, который позволит вам запускать серверные страницы в отладчике. Это всегда полезная возможность!

Внутри страницы EchoWebService.ashx вы обнаружите класс EchoWebSocket, написанный на C#. У него есть один метод, ProcessRequest, который обрабатывает первоначальный HTTP-запрос от клиента веб-сокета. С помощью запроса он получает сокет, записывает пригласительное сообщение в поток сокета, когда сокет открывается, и затем ожидает принимать другие сообщения. Если он принимает текстовое сообщение, он возвращает это же сообщение назад через сокет, добавив в начале "You said". Если он получает двоичное сообщение, он возвращает сообщение, указав объем принятых данных.

Переходя в Сценарий 1 примера "Соединение с WebSocket", мы можем отправить сообщение серверной страницы, используя MessageWebSocket, и получить это сообщение в ответ; смотрите рис. 9.1 В данном случае выходные данные примера отражают информацию, известную приложению и ничего от самого сервиса.

Выходные данные Сценария 1 примера "Соединение с WebSocket"

Рис. 9.1. Выходные данные Сценария 1 примера "Соединение с WebSocket"

В примере, мы сначала создаём a MessageWebSocket, вызываем его метод connectAsync, и затем используем DataWriter для записи данных в сокет. В примере так же прослушивается событие messagereceived для вывода результатов отправки, и прослушивается событие closed от сервера, таким образом, клиент может выполнить метод close и со своей стороны. Вот упрощенный вариант кода из js/scenario1.js:

  var messageWebSocket;
  var messageWriter;

  var webSocket = new Windows.Networking.Sockets.MessageWebSocket();
  webSocket.control.messageType = Windows.Networking.Sockets.SocketMessageType.utf8;
  webSocket.onmessagereceived = onMessageReceived;
  webSocket.onclosed = onClosed;

  // Здесь получают и проверяют URI сервера, после чего сохраняют в переменной uri.

  webSocket.connectAsync(uri).done(function () {
  messageWebSocket = webSocket;
  // Кодировка DataWriter по умолчанию – utf8.
  messageWriter = new Windows.Storage.Streams.DataWriter(webSocket.outputStream);
  sendMessage();   // Вспомогательная функция, смотрите ниже
  }, function (error) {
  var errorStatus = Windows.Networking.Sockets.WebSocketError.getStatus(error.number);
  // [Вывод сообщения об ошибке]
  });

  function onMessageReceived(args) {
  var dataReader = args.getDataReader();
  // [Вывод содержимого сообщения]
  }

  function sendMessage() {
  // Запись сообщения во входное поле сокета
  messageWriter.writeString(document.getElementById("inputField").value);
  messageWriter.storeAsync().done("", sendError);
  }

  function onClosed(args) {
  // Закрываем наш сокет, если сокет сервера закрыт [упрощение из исходного примера; он так же закрывает
  // DataWriter, который он мог открыть.]
  messageWebSocket.close();
  }

Похоже на ранее рассмотренные примеры то, что когда происходит ошибка, вы можете преобразовать номер ошибки в значение SocketErrorStatus. (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.socketerrorstatus.aspx). В случае с веб-сокетами, это делается с помощью метода getStatus объекта Windows.Networking.Sockets.WebSocketError ( http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.websocketerror.aspx). Для того, чтобы узнать подробности, смотрите страницы справки.

Сценарий 2, в свою очередь, использует StreamWebSocket для отправки непрерывного потока пакетов данных, этот процесс будет продолжаться до тех пор, пока вы не закроете соединение, смотрите рис. 9.2.

Выходные данные Сценария 2 примера "Соединение с WebSocket" (окно обрезано)

Рис. 9.2. Выходные данные Сценария 2 примера "Соединение с WebSocket" (окно обрезано)

Вот как это выглядит в коде, взятом из js/scenario2.js, где мы видим шаблон, похожий на только что рассмотренный для MessageWebSocket, только здесь производится отправка непрерывного потока данных:

  var streamWebSocket;
  var dataWriter;
  var dataReader;
  var data = "Hello World";
  var countOfDataSent;
  var countOfDataReceived;

  var webSocket = new Windows.Networking.Sockets.StreamWebSocket();
  webSocket.onclosed = onClosed;

  // Здесь получают и проверяют URI сервера, после чего сохраняют в переменной uri.

  webSocket.connectAsync(uri).done(function () {
  streamWebSocket = webSocket;
  dataWriter = new Windows.Storage.Streams.DataWriter(webSocket.outputStream);
  dataReader = new Windows.Storage.Streams.DataReader(webSocket.inputStream);
  // Когда происходит буферизация, возвращаем как только будут доступны какие-либо данные.
  dataReader.inputStreamOptions = Windows.Storage.Streams.InputStreamOptions.partial; countOfDataSent = 0;
  countOfDataReceived = 0;

  // Непрерывная отправка данных на сервер
  writeOutgoing();

  // Непрерывно прослушиваем в ожидании ответа
  readIncoming();
  }, function (error) {
  var errorStatus = Windows.Networking.Sockets.WebSocketError.getStatus(error.number);
  // [Вывод сообщения об ошибке]
  });

  function writeOutgoing() {
  try {
  var size = dataWriter.measureString(data);
  countOfDataSent += size;
  } dataWriter.writeString(data); dataWriter.storeAsync().done(function () {
  // Добавляем 1-секундную задержку, чтобы пользователь видел что происходит.
  setTimeout(writeOutgoing, 1000);
  }, writeError);
  }
  catch (error) {
  // [Вывод сообщения об ошибке]
  }
  }

  function readIncoming(args) {
  // Буферизуйте столько данных, сколько нужно вашему протоколу.
  dataReader.loadAsync(100).done(function (sizeBytesRead) {
  countOfDataReceived += sizeBytesRead;
  // [Вывод количества]

  var incomingBytes = new Array(sizeBytesRead);
  dataReader.readBytes(incomingBytes);

  // Сделайте что-нибудь с данными. В качестве альтернативы вы можете использовать DataReader для чтения
  // отдельных логических значений, целых чисел, строк и так далее.
  // Начало новой операции чтения.
  readIncoming();
  }, readError);
  }

  function onClosed(args) {
  // [Остальной код опущен, включая закрытие DataReader и DataWriter]
  streamWebSocket.close();
  }

Как и в случае с обычными сокетами, вы можете осуществить дополнительную настройку WebSockets, в том числе – задать учетные данные и указать поддерживаемые протоколы с помощью свойства control объектов MessageWebSocket и StreamWebSocket. Для того, чтобы узнать подробности, обратитесь к материалу "Использование расширенных элементов управления WebSocket" (http://msdn.microsoft.com/library/windows/apps/hh761447.aspx). Похожим образом, вы можете настроить безопасное/зашифрованное соединение с использованием схемы URI wss:// вместо ws:// которая использована в примере. Обратитесь к материалу "Обеспечение безопасности подключений WebSocket с помощью протокола TLS/SSL" (http://msdn.microsoft.com/library/windows/apps/hh761446.aspx)

Фоновая задача ControlChannelTrigger

В Главе 2 мы видели класс Windows.Networking.Sockets.ControlChannelTrigger (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.controlchanneltrigger.aspx), который можно использовать для настройки фоновой задачи для уведомлений реального времени, как используется VoIP, IM, электронной почтой, и другими сценариями "постоянной доступности". Повторюсь, работа с каналом управления – это не то, что можно сделать из JavaScript, поэтому обратитесь к материалу "Настройка параметров фонового подключения" (http://msdn.microsoft.com/library/windows/apps/Hh771189.aspx) и к следующим примерам на C#/C++

Неоконченные дела (или некоторые примеры для разбора)

Хотя в этой лекции мы рассмотрели довольно много по заданной теме, некоторые дополнительные примеры могут оказаться полезными при разработке ваших сетевых программ. Я больше не буду касаться их в дальнейшем, но этот список, по крайней мере, поможет вам узнать об их существовании.

Пример Описание (из Центра разработчиков Windows)
"Проверка того, является ли текущий сеанс работы удаленным" (http://code.msdn.microsoft.com/windowsapps/Check-if-current-session-7cd31c4e) Пример демонстрирует использование API Windows.System.RemoteDesktop (http://msdn.microsoft.com/library/windows/apps/Hh770630). В частности, данный пример показывает как использовать свойство InteractiveSession.IsRemote (http://msdn.microsoft.com/library/windows/apps/Hh770629) для определения того, является ли текущий сеанс работы удаленным.
"Приложение HomeGroup" (http://code.msdn.microsoft.com/windowsapps/HomeGroup-App-sample-d4da5cb2) Показывает, как использовать папку HomeGroup для открытия и поиска файлов, а так же предоставления к ним общего доступа. Пример использует некоторые возможности HomeGroup. В частности, использует перечисление Windows.Storage.Pickers.PickerLocationId и свойство Windows.Storage. KnownFolders.homeGroup для выбора файлов, которые содержатся в папке HomeGroup.
"Клиент контейнера приложений удаленного рабочего стола" (http://code.msdn.microsoft.com/windowsapps/Remote-Desktop-app-461567af) Показывает, как использовать объект клиента контейнера приложения удаленного рабочего стола (http://msdn.microsoft.com/library/windows/apps/Hh994983) в приложении.
"API рабочего пространства соединения RemoteApp и рабочего стола" (http://code.msdn.microsoft.com/windowsapps/RemoteApp-and-Desktop-cb639443) Показывает, как использовать объект WorkspaceBrokerAx (http://msdn.microsoft.com/library/windows/apps/Hh974747) в приложениях для Windows 8.
"Отправка и прием SMS-сообщений и управление SIM-картой" ( http://code.msdn.microsoft.com/windowsapps/Sms-SendReceive-fa02e55e) Показывает, как использовать API Windows 8 по работе с SMS для мобильных широкополосных устройств (Windows.Devices.Sms (http://msdn.microsoft.com/library/windows/apps/BR206567)). Данное API может быть использовано только приложениями для мобильных широкополосных устройств и не доступно обычным приложениям.
"Фоновая задача SMS" (http://code.msdn.microsoft.com/windowsapps/SMS-background-task-sample-513576cb) Показывает, как использовать API Windows 8 по работе с SMS для мобильных широкополосных устройств (Windows.Devices.Sms (http://msdn.microsoft.com/library/windows/apps/BR206567)) с API фоновых задач (Windows.ApplicationModel.Background (http://msdn.microsoft.com/library/windows/apps/BR224847)) для отправки и приема текстовых SMS-сообщений. Данное API может быть использовано только приложениями для мобильных широкополосных устройств и не доступно обычным приложениям.
"Управление с помощью USSD-сообщений" (http://code.msdn.microsoft.com/windowsapps/USSD-API-SDK-Sample-b0259f6c) Демонстрирует управление сетевой учетной записью с использованием протокола USSD на мобильном широкополосном устройстве, поддерживающем GSM. USSD обычно используется для управления учетными записями мобильных широкополосных профилей операторов мобильных сетей Mobile Network Operator (MNO). USSD-сообщения зависят от конкретного MNO, их следует соответствующим образом выбирать при работе в реальной сети. [Данный пример применим только к приложениям операторов для мобильных широкополосных устройств, он использует API в Windows.Networking.NetworkOperators (http://msdn.microsoft.com/library/windows/apps/BR241148).]

Что мы только что изучили

  • Существуют различные виды сетей, и в манифесте приложения можно объявлять разные возможности, в частности, Интернет (Клиент) (Internet (Client)), Интернет (клиент и сервер) Internet (Client & Server) и Частные сети (клиент и сервер) (Private Networks (Client & Server)). Локальная обратная петля при этом обычно заблокирована для приложений, но её можно использовать для целей отладки на компьютере с лицензией разработчика.
  • Подробная сетевая информация доступна с помощью API Windows.Networking.Connectivity.NetworkInformation, включая возможность отслеживать состояние соединения, получать сведения о стоимости соединения и получать сведения о профиле соединения.
  • Состояние подключения можно отслеживать из фоновой задачи с использованием триггера networkStateChange и условий, таких, как internetAvailable и internetNotAvailable.
  • Возможность исполняться при отсутствии подключения к сети – это важное соображение, которое может сделать приложение более привлекательным для пользователей. Приложения реализуют подобную функциональность самостоятельно, используя локальную или временную папку данных приложения для хранения необходимых кэшированных данных.
  • Windows.Networking.BackgroundTransfer предоставляет средства для организации загрузок и отправок данных с учетом стоимости соединения, которые продолжают выполняться, когда приложение приостановлено, и которые легко можно возобновить, если приложение запущено после остановки. Использование этих API настоятельно рекомендовано, вместо реализации того же самого с помощью XmlHttpRequest. Это API поддерживает учетные данные, составную отправку данных, политику тарификации и группировку.
  • Пользовательский интерфейс средства выбора учетных данных предоставляет встроенный интерфейс для сбора учетных данных, а хранилище учетных данных предоставляет безопасные средства для хранения и получения этих данных (которые, так же, могут быть перемещены, если это разрешено пользователем, на другое доверенное устройство пользователя).
  • Приложения могут проходить проверку подлинности с помощью поставщиков OAuth с использованием API брокера веб-проверки подлинности. Это позволяет приложениям получать необходимые ключи доступа и маркеры у этих поставщиков, никогда не сталкиваясь с необходимостью самостоятельно управлять учетными данными пользователя.
  • Для провайдеров проверки подлинности, которые это поддерживать, приложения могут использовать режим единого входа, то есть, аутентификация пользователя в одном приложении приведет к аутентификации его в других приложениях, которые используют того же поставщика. Live SDK/Live Connect предоставляют такую возможность с учетной записью Microsoft пользователя.
  • Приложения могут получать некоторые данные профиля пользователя и управлять ими, включая изображение пользователя и изображение экрана блокировки.
  • WinRT предоставляет API для шифрования, расшифровки и работы с сертификатами.
  • API Windows.Web.Syndication предоставляет структурированный способ работы с RSS-каналами, API Windows.Web.AtomPub предоставляет структурированные средства для отправки и редактирования записей, а так же для управления ими.
  • Поддержка сокетов в WinRT включает в себя поддержку сокетов датаграмм и потоков, а так же – веб-сокетов сообщений и потоков. Возможности последних расширяют возможности веб-сокетов W3C поддержкой и потоковой (TCP) модели и двоичного содержимого.
< Лекция 8 || Лекция 9: 1234 || Лекция 10 >