Программная логика приложений для Windows 8 и их взаимодействие с системой

Синдикация

Разбить на страницы
Показывать лекцию целиком

Когда мы впервые столкнулись с выполнением запросов XmlHttpRequests с помощью WinJS.XHR в Главе 3 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", мы запрашивали RSS-ленту из Блога для разработчиков приложений для Windows 8, пользуясь URI http://blogs.msdn.com/b/windowsappdev/rss.aspx . Затем мы узнали, что WinJS.xhr возвращает promise-объект, результат которого содержит свойство responseXML , которое имеет тип DomParser, с его помощью можно обойти структуру DOM и так далее.

Работа с синдицированными веб-каналами, наподобие вышеупомянутой, полностью поддерживается приложениями для Магазина Windows. На самом деле, материал "Доступ к сводному содержимому и управление им" (http://msdn.microsoft.com/ru-ru/library/windows/apps/hh452973.aspx) описывает именно этот процесс, компоненты которого показаны в примере "Интеграция содержимого и элементов управления веб-сервисов" (http://code.msdn.microsoft.com/windowsapps/Mashup-Sample-10689f5b).

Тем не менее, WinRT предлагает дополнительные API для работы с синдицированным содержимым. Один из них, Windows.Web.Syndication, предлагает более структурированный подход для работы с RSS-каналами. Другой, Windows.Web.AtomPub, предоставляет средства для публикации записей каналов и управления ими. И то и другое предоставлены в WinRT для языков, у которых нет средств для достижения тех же целей, но, как у разработчика, работающего с JavaScript, у вас есть выбор.

Чтение RSS-каналов

Основной класс в Windows.Web.Syndication (http://msdn.microsoft.com/library/windows/apps/br244529.aspx) это ). Для работы с любым каналом сначала создают экземпляр этого класса и задают необходимые свойства. Это следующие свойства: serverCredential (типа PasswordCredential), proxyCredential (еще одно свойство типа PasswordCredential), timeout (в миллисекундах, по умолчанию 30000 или 30 секунд), maxResponseBufferSize (средство для защиты от потенциальных серверов злоумышленников), и bypassCacheOnRetrieve (логическое значение, которое указывает на то, всегда ли нужно получать новые данные с сервера ). Так же можно произвести необходимое количество вызовов его метода setRequestHeader (передавая имя и значение) для настройки заголовка XmlHttpRequest.

Последний шаг заключается в вызове метода ), который получает RSS-канал блога "Создание Windows 8" (http://blogs.msdn.com/b/b8/rss.aspx):

 uri = new Windows.Foundation.Uri("http://blogs.msdn.com/b/b8/rss.aspx");

   var client = new Windows.Web.Syndication.SyndicationClient();
   client.bypassCacheOnRetrieve = true;
   client.setRequestHeader("User-Agent",
   "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");

    client.retrieveFeedAsync(uri).done(function (feed) {
    // feed это объект SyndicationFeed

Результат выполнения ), то есть, SyndicationClient это то, что вы использовали для взаимодействия с свервисом, и когд вы получаете канал, вы получаете объект, с помощью которого вы можете затем обработать полученные данные. Если вы взглянете на SyndicationFeed , воспользовавшись вышеприведенной ссылкой, вы увидите, что он наполнен свойствами, которые представляют собой все части канала, такие, как authors, categories, items, title, и так далее. Некоторые из них представлены другими классами в Windows.Web.Syndication, или их коллекциями, когда более простых типов недостаточно: SyndicationAttribute, SyndicationCategory, SyndicationContent, SyndicationGenerator, SyndicationItem, SyndicationLink, SyndicationNode, SyndicationPerson, и SyndicationText. Оставляю разъяснение подробностей обо всём этом документации.

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

client.retrieveFeedAsync(uri).done(function (feed) {
  currentFeed = feed;

  var title = "(no title)";

  // currentFeed.title это объект SyndicationText
  if (currentFeed.title) {
  title = currentFeed.title.text;
  }
  // currentFeed.items это коллекция SyndicationItem (массив)
  currentItemIndex = 0;
  if (currentFeed.items.size>0) {
  displayCurrentItem();
  }
  }

  // ...

  function displayCurrentItem() {
  // Элемент будет иметь тип SyndicationItem

  var item = currentFeed.items[currentItemIndex];

  // Отображение номера элемента.
  document.getElementById("scenario1Index").innerText = (currentItemIndex + 1) + " of "
  + currentFeed.items.size;

  // Отображение заголовка (item.title это еще один SyndicationText).
  var title = "(no title)";
  if (item.title) {
  title = item.title.text;
  }
  document.getElementById("scenario1ItemTitle").innerText = title;

  // Отображение основной ссылки (item.links это коллекция объектов SyndicationLink).
  var link = "";
  if (item.links.size>0) {
  link = item.links[0].uri.absoluteUri;
  }

  var scenario1Link = document.getElementById("scenario1Link");
  scenario1Link.innerText = link;
  scenario1Link.href = link;

  // Отображение тела в виде HTML (item.content это объект SyndicationContent, item.summary это
  // объект SyndicationText object).
  var content = "(no content)";
  if (item.content) {
  content = item.content.text;
  }
  else if (item.summary) {
  content = item.summary.text;
  }
  document.getElementById("scenario1WebView").innerHTML = window.toStaticHTML(content);

  // Отображение расширений элемента. Коллекция elementExtensions содержит дополнительные
  // дочерние элементы в текущем элементе, которые не принадлежат станартам Atom или RSS
  // (например, элементы расширения Dublin Core). Создавая их массив, мы можем создать
  // WinJS.Binding.List, который можно легко вывести в ListView.
  var bindableNodes = [];
  for (var i = 0; i<item.elementExtensions.size; i++) {
var bindableNode = {
nodeName: item.elementExtensions[i].nodeName, nodeNamespace: 
item.elementExtensions[i].nodeNamespace, nodeValue: 
item.elementExtensions[i].nodeValue,
};
bindableNodes.push(bindableNode);
}
var dataList = new WinJS.Binding.List(bindableNodes);
var listView = document.getElementById("extensionsListView").winControl; 
WinJS.UI.setOptions(listView, { itemDataSource: dataList.dataSource });
}

Видимо, это очевидно, что API, лежащие в основе всего этого, скорее всего, просто используют API XmlDocument для получения этих свойств канала. На самом деле его getXmlDocument возвращает этот XmlDocument, если вам понадобится доступ к нему в собственных целях.

Так же вы можете создать объект SyndicationFeed на основе XML для канала, который у вас уже есть. Например, если вы получили содержимое канала с использованием WinJS.xhr, вы можете создать новый объект SyndicationFeed и вызвать его метод load со свойством XHR responseXML. Затем вы можете работать с каналом с помощью иерархии класса. При использовании API Windows.Web.AtomPub для управления каналом, вы так же создаете новый или обновленный элемент SyndicationItem для передачи по каналам связи, устанавливаете его значения посредством других объектов в его иерархии. Скоро мы это увидим.

Еще одно примечание: если retrieveFeedAsync выдает исключение, которое можно получить с помощью обработчика ошибок, который вы предоставили в метод done соответствующего promise-объекта, вы можете преобразовать код ошибки в значение SyndicationErrorStatus. Вот, как это используется в обработчике ошибок примера:

       
function onError(err) {
// Сранивает номер ошибки со значением SyndicationErrorStatus. Используйте
// Windows.Web.WebErrorStatus.getStatus() для получения кодов статусов HTTP - ошибок.
var errorStatus = Windows.Web.Syndication.SyndicationError.getStatus(err.number);
if (errorStatus === Windows.Web.Syndication.SyndicationErrorStatus.invalidXml) {
displayLog("An invalid XML exception was thrown. Please make sure to use a URI that"
+ "points to a RSS or Atom feed.");
}
}

Использование AtomPub

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

API для этого можно найти в Windows.Web.AtomPub (http://msdn.microsoft.com/library/windows/apps/windows.web.atompub.aspx), это показано в примере "AtomPub" (http://code.msdn.microsoft.com/windowsapps/AtomPub-sample-c1fcdc8e). Главный класс здесь – это ), который инкапсулирует методы, необходимые для реализации протокола AtomPub. У него есть методы наподобие createResourceAsync, retrieveResourceAsync, updateResourceAsync, и deleteResourceAsync для работы с записями, где каждый ресурс идентифицируется с помощью URI и объекта SyndicationItem, при необходимости. Мультимедиа-ресурсами для записей управляют с помощью createMediaResourceAsync и похожим образом названных методов,где ресурс представляется в виде объекта IInputStream.

У объекта AtomPubClient так же имеются методы retrieveFeedAsync и setRequestHeader, которые выполняют те же функции, что и методы SyndicationClient с такими же именами, а так же есть несколько похожих свойств, наподобие serverCredential, timeout, и bypassCacheOnRetrieve. Еще один метод, retrieveServiceDocumentAsync, предоставляет документацию рабочего пространства/сервиса для канала (в форме объекта Windows.Web.AtomPub.ServiceDocument).

Снова, пример "AtomPub" ( http://code.msdn.microsoft.com/windowsapps/AtomPub-sample-c1fcdc8e). показывает различные операции: получение данных (Сценарий 1), создание (Сценарий 2), удаление (Сценарий 3) и обновление (Сценарий 4). Вот как, для начала, создаётся объект AtomPubClient (смотрите js/common.js), наличие учетных данных предполагается:

  function createClient() {
  client = new Windows.Web.AtomPub.AtomPubClient();
  client.bypassCacheOnRetrieve = true;
  var credential = new Windows.Security.Credentials.PasswordCredential();
  credential.userName = document.getElementById("userNameField").value;
  credential.password = document.getElementById("passwordField").value;
  client.serverCredential = credential;
  }
  Обновление записи (js/update.js) выглядит так, где обновление представлено вновь созданным объектом SyndicationItem:
  function getCurrentItem() {
  if (currentFeed) {
  return currentFeed.items[currentItemIndex];
  }
  return null;
  }
  var resourceUri = new Windows.Foundation.Uri( /* service address */ );
  createClient();

  var currentItem = getCurrentItem();

  if (!currentItem) {
  return;
  }

  // Обновление элемента
  var updatedItem = new Windows.Web.Syndication.SyndicationItem();
  var title = document.getElementById("titleField").value;
  updatedItem.title = new Windows.Web.Syndication.SyndicationText(title,
  Windows.Web.Syndication.SyndicationTextType.text);
  var content = document.getElementById("bodyField").value;
  updatedItem.content = new Windows.Web.Syndication.SyndicationContent(content,
  Windows.Web.Syndication.SyndicationTextType.html);

  client.updateResourceAsync(currentItem.editUri, updatedItem).done(function () {
  displayStatus("Updating item completed.");
  }, onError);

Обработка ошибок в данном случае работает с классом ) (смотрите js/common.js):

function onError(err) {
  displayError(err);

  // Сравнивает номер ошибки со значением WebErrorStatus, для того, чтобы иметь дело с конкретной ошибкой.
  var errorStatus = Windows.Web.WebError.getStatus(err.number);
  if (errorStatus === Windows.Web.WebErrorStatus.unauthorized) {
  displayLog("Wrong username or password!");
  }
  }

Сокеты

Сокеты – это основа сетевого транспорта. В отличие от HTTP-запросов, где клиент отправляет запрос серверу, а сервер отвечает ему, то есть – от отдельного сеанса взаимодействия, сокеты – это соединение между клиентским и серверным IP-портами, так что каждый может в любое время отправлять другому данные по этому каналу. Очевидно, мы видели подобный механизм раньше, а именно, когда использовали Windows Push Notification Service (WNS). WNS, однако, ограничен уведомления и специально создан для отправки обновлений плиток или уведомлений для приложений, которые не исполняются. Сокеты, с другой стороны, созданы для обмена данными между сервером и работающим клиентом.

Сокеты обычно используют, когда нет API более высокого уровня или других абстрактных механизмов, подходящих для конкретного сценария, когда используются пользовательские протоколы, когда нужна возможность двусторонней связи, или когда важно снизить нагрузку на сеть при каждом обмене данными. Если рассмотреть HTTP, это протокол, который построен на низкоуровневых сокетах. Отдельный HTTP-запрос обычно включает в себя заголовки и множество других данных, помимо небольшого объема полезного содержимого, поэтому это – неэффективный сетевой транспорт, если нужно передать множество небольших фрагментов информации. Лучше – подключиться напрямую к серверу и передавать данные с минимальным количеством служебной информации. VoIP – другой пример, где хорошо работают сокеты, как и широковещательные сценарии наподобие многопользовательских игр. В последних один из компьютеров игроков выполняет роль сервера в локальной подсети, он может передавать сообщения всем остальным игрокам, и наоборот, опять же, с минимальной избыточностью.

В мире сокетов обмен данными может происходить двумя путями: в виде отдельных пакетов или сообщений (напоминающих ведра с водой), или в виде непрерывного потока (как вода, текущая по трубе). Их называют сокетами датаграмм и сокетами потоков, соответственно, и то и другое поддерживается посредством API WinRT. WinRT так же поддерживает обе формы обмена данными посредством проткола WebSocket, технологии, изначально созданной для веб-браузеров и веб-серверов, которая стала весьма интересной для применения в приложениях, для общих целей. Все применимые классы находятся в API Windows.Networking.Sockets (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.aspx), как мы увидим в следующих разделов. Обратите на это внимание, так как имеется некоторое наложение между разными типами сокетов, эти разделы нужно читать по порядку, так как я не собираюсь повторяться!

Сокеты датаграмм

На языке сокетов ведро с водой называется датаграммой. Это пакет данных, отправленных с одного конца сокета на другой – даже без наличия соответствующего соединения. В соответствии со стандартом User Datagram Protocol (UDP). UDP, как я обобщаю здесь из его описания в Википедии (http://ru.wikipedia.org/wiki/UDP), это простой, не предусматривающий сохранение состояния, двунаправленный протокол, ориентированный на отдельные транзакции. Он подразумевает минимальную избыточность и отсутствие задержек повторной передачи данных, и поэтому он не может гарантировать, что датаграмма будет доставлена. Таким образом он используется тогда, когда в проверке ошибок и коррекции нет необходимости, или это выполняется самим приложением, а не на уровне сетевого интерфейса. Например, сценарий использования VoIP позволяет просто терять пакеты, если они не могут быть доставлены, вместо того, чтобы заставлять все остальные механизмы ждать задержавшегося пакета. В результате качество звука может страдать, но говорящие не начнут заикаться или разговаривать как пришельцы из другой галактики. Коротко говоря, UDP может быть ненадежным, но он минимизирует задержки передачи данных. Протоколы более высокого уровня, наподобие Real-time Transport Protocol (RTP) и Real Time Streaming Protocol (RTSP) построены на базе UDP.

Приложения для Магазина Windows работают с этим средством передачи данных, либо в роли клиента, либо как сервер, используя класс ), объект, который нужно инициализировать с использованием оператора new для настройки соединения и ожидания поступления сообщений:

var listener = new Windows.Networking.Sockets.DatagramSocket();

С каждой стороны соединения, следующий шаг – это прослушивание события этого объекта ):

     // Событие из WinRT: помните о вызове removeEventListener при необходимости
      listener.addEventListener("messagereceived", onMessageReceived);

Когда данные прибывают, обработчик получает то, чего ждет – объект DatagramSocketMessageReceivedEventArgs ( http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.datagramsocketmessagereceivedeventargs.aspx) (и не выговоришь). Он содержит свойства ), которые содержат IP-адрес, отображаемое имя и некоторые другие данные. Смотрите раздел "Информация о сети (Реестр сетевых объектов)" выше в этой лекции чтобы узнать подробности. Аргументы события, так же, содержат строку ), который возвращает IInputStream с помощью которого можно прочесть последовательно расположенные байты данных.

Другой – ), возвращает объект ), высокоуровневую абстракцию, построенную на основе IInputStream, которая помогает считывать конкретные типы данных непосредственно. Очевидно, если вам известна структура данных, которую вы ожидаете получить в сообщении, использование DataReader освободит вас от необходимости самостоятельно выполнять преобразование типов данных.

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

  • connectAsync Запускает операцию соединения с использованием заданного объекта HostName и имени службы (или строкового представления UDP-порта) удаленного сетевого пункта назначения. Этот метод используется для создания одностороннего соединения клиента с сервером.
  • Другая форма ), который задаёт имя узла и службы и для локальной и для удалённой конечных точек сетевого подключения. Он используется для создания двустороннего соединения клиента и сервера, в то время как локальная конечная точка означает вызов bindEndpointAsync, как показано ниже.
  • bindEndpointAsync Используется для одностороннего серверного соединения, то есть – только для прослушивания, но не для отправки данных в сокет – этот метод просто привязывает локальную конечную точку, заданную в HostName и имени службы или порта. Привязка службы может быть реализована с помощью bindServiceNameAsync.
  • joinMulticastGroup Принимает HostName, присоединяет объект сокета датаграмм к группе многоадресной рассылки.
  • close Прекращает соединение и прерывает все ожидающие операции.
  • Совет. Для того, чтобы открыть сокет на порту локального хоста для целей отладки, воспользуйтесь connectAsync следующим образом:

      var socket = new Windows.Networking.Sockets.DatagramSocket();
      socket.connectAsync(new Windows.Networking.Sockets.DatagramSocket("localhost",
      "12345", Windows.Networking.Sockets.SocketProtectionLevel.plainSocket)
      .done(function () {
      // ...
      }, onError);

    Обратите внимание на то, что так как каждый сокет может быть подключен к любому количеству конечных точек, вы можете вызывать метод connectAsync много раз, присоединяться к множеству групп многоадресной рассылки и привязывать множество локальных конечных точек с помощью bindEndpointAsync и bindServiceNameAsync. Метод close, учтите, закрывает сразу всё!

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

    Управляющие данные могут быть заданы с помощью свойства DatagramSocket.control, которое является объектом

    ) со свойствами ) и ).

    Всё это, конечно, лишь предварительная подготовка к передаче данных по соединению сокета. Это выполняется с помощью свойства ) типа IOutputStream (http://msdn.microsoft.com/library/windows/apps/windows.storage.streams.ioutputstream.aspx), куда вы записываете любые необходимые даные с использованием его методов writeAsync и flushAsync. Благодаря этому данные можно отправить по любому из соединений сокета. Так же можно использовать один из вариантов getOutputStreamAsync для задания конкретных параметров EndpointPair или HostName/порта на которые нужно отправить данные. Результат каждой из этих асинхронных операций – это, снова, IOutputStream. И во всех случаях вы можете создавать высокоуровневый объект DataWriter на основе данного потока:

        var dataWriter = new Windows.Storage.Streams.DataWriter(socket.outputStream)

    Вот, как это всё показано в примере "DatagramSocket" ( http://code.msdn.microsoft.com/windowsapps/DatagramSocket-sample-76a7d82b), небольшом приложении, в котором вам нужно последовательно запустить каждый из сценариев. Сценарий 1, в первую очередь, устанавливает серверный прослушиватель на локальный хост, используя номер порта 22112 (имя службы) по умолчанию. Для того чтобы это сделать, он создает сокеты, добавляет прослушиватель и вызывает bindServiceNameAsync (js/startListener.js):

    socketsSample.listener = new Windows.Networking.Sockets.DatagramSocket();
      // Напоминание: вызовите при необходимости removeEventListener; это может быть обычной процедуре при работе с сокетами,
      // которая сопровождает жизненный цикл приложения.
      socketsSample.listener.addEventListener("messagereceived", onServerMessageReceived);
    
      socketsSample.listener.bindServiceNameAsync(serviceName).done(function () {
      // ...
      }, onError);

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

      function onServerMessageReceived(eventArgument) {
      // [Код здесь проверяет, есть ли уже у нас выходной поток]
    
      socketsSample.listener.getOutputStreamAsync(eventArgument.remoteAddress,
      eventArgument.remotePort).done(function (outputStream) {
      // [Сохраняем выходной поток с некоторыми другими данными, опущено]
      socketsSample.listenerOutputStream = outputStream;
      }
    
      // Это вспомогательная функция
      echoMessage(socketsSample.listenerOutputStream, eventArgument);
      });
      }
      // eventArgument здесь - это DatagramSocketMessageReceivedEventArgs с методом getDataReader method
      function echoMessage(outputStream, eventArgument) {
      // [некоторый код для вывода информации опущен]
    
      // Получаем поток сообщения из DataReader и отправляем его в выходной поток
      outputStream.writeAsync(eventArgument.getDataReader().detachBuffer()).done(function () {
      // Ничего не делаем – клиент выведет сообщение при получении данных.
      });
      }

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

    Сценарий 2 устанавливает прослушиватель на локальный хост, на тот же порт. С этой стороны, мы так же создаем DatagramSocket и настраиваем прослушиватель для messagereceived. Эти сообщения, как то, что записывается в выходной поток на серверной стороне, как мы только что видели – принимаются в обработчике события, код которого приведен ниже (js/connectToListener.js), который использует DataReader для извлечения и отображения сообщения:

      function onMessageReceived(eventArgument) {
      try {
      var messageLength = eventArgument.getDataReader().unconsumedBufferLength;
      var message = eventArgument.getDataReader().readString(messageLength);
       socketsSample.displayStatus("Client: receive message from server \"" + message + "\"");
      } catch (exception) {
      status = Windows.Networking.Sockets.SocketError.getStatus(exception.number);
      // [Отображение подробностей об ошибке]
      }
      }

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

    Даже после того, как всё вышеописанное сделано, ничего пока не происходит, так как мы еще не отправили данные! Переход к Сценарию 3 и нажатие его кнопки Send ‘Hello’ Now выполняет действия от имени клиента:

          // [Это происходит после проверки действительности сокета]
          socketsSample.clientDataWriter =
          new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
    
          var string = "Hello World";
          socketsSample.clientDataWriter.writeString(string);
    
          socketsSample.clientDataWriter.storeAsync().done(function () {
          socketsSample.displayStatus("Client sent: " + string + ".");
          }, onError);

    Вызов DataWriter.storeAsync это то, что осуществляет запись в поток в сокете. Если вы установите здесь точку останова и в обоих обработчиках события messagereceived, вы увидите, что storeAsync генерирует сообщение для сервера, когда точка останова сработает в onServerMessageReceived в js/startListener.js. Затем будет осуществлена запись обратно в сокет, что вызовет остановку в onMessageReceived в js/connectToListener.js, что приведет к выводу сообщения. (И, для того, чтобы завершить этот процесс, Сценарий 4 предоставляет кнопку для вызова метода сокета close.)

    Пример выполняет всё с одним и тем же приложением на локальном хосте, что даёт простую возможность увидеть, как всё это работает. Обычно, конечно, сервер будет исполняться на другом компьютере, но шаги по настройке прослушивателей в подобном случае выглядят так же. Как отмечено в Главе 2, соединения локального хоста работают лишь на компьютере с лицензией разработчика и не будут работать для приложений, полученных из Магазина Windows.

    Сокеты потока

    В отличие от сокетов датаграмм, потоковая передача данных через сокеты Transmission Control Protocol(TCP). Отличительная черта TCP заключается в точной и надёжной доставке – он гарантирует, что принятые байты соответствуют отправленным: когда пакеты передаются по сети, TCP пытается повторять их передачу, если что-то пошло не так. Именно поэтому он является частью TCP/IP, что дает нам Всемирную паутину, электронную почту, передачу файлов и многое другое. HTTP, SMTP, и Session Initiation Protocol (SIP) так же построены на основе TCP. Во всех случаях, клиенты и сервера видят лишь надёжный поток данных, путешествующих от одного конца соединения к другому.

    В отличие от сокетов датаграмм, для которых имеется лишь один класс WinRT, для обеих сторон соединения, сокеты потока различаются сильнее, для соответствия уникальным нуждам клиентской и серверной ролей. На клиентской стороне это ); на серверной – StreamSocketListener (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.streamsocketlistener.aspx).

    Начнем с последнего, объект StreamSocketListener очень похож на DatagramSocket, который мы только что рассмотрели в предыдущем разделе, он имеет следующие методы, свойства и события:

  • ), содержащий строку localPort.
  • ) со свойством qualityOfService.
  • ) содержат единственное свойство, socket. Это StreamSocket для клиента, в котором есть свойство outputStream , из которого прослушиватель может получить поток данных.
  • bindEndpointAsync и bindServiceNameAsync Привязывают прослушиватель к HostName и имени службы, или осуществляют привязку лишь к имени сервиса.
  • close Прекращает соединение и прерывает все ожидающие операции.
  • Со стороны клиента, )) и )) и вездесущему методу close, мы обнаруживаем несколько вполне обычных и один необычный:

  • ), который может принимать значения plainSocket, ssl, или sslAllowNullEncryption. Это, другими словами, четыре варианта данного метода.
  • inputStream Обект IInputStream который принимает данные через соединению.
  • outputStream Объект IOutputStream в который осуществляется запись данных.
  • ) Обновляет соединение типа plainSocket (созданное с помощью connectAsync) для использования SSL, что задаётся либо SocketProtectionLevel.ssl либо sslAllowNullEncryption. Эти методы так же требуют HostName, который подтверждает соединение.
  • Для того, чтобы узнать больше об использовании SSL, смотрите материал "Обеспечение безопасности подключений через сокеты с помощью протокола TLS/SSL" (http://msdn.microsoft.com/library/windows/apps/hh780595.aspx).

    В любом случае, вы можете видеть, что для односторонней связи с использованием TCP приложение создает либо StreamSocket либо StreamSocketListener, в зависимости от его роли. Для двусторонней связи приложению нужно и то и другое.

    Пример "StreamSocket" (http://code.msdn.microsoft.com/windowsapps/StreamSocket-Sample-8c573931), как и пример "DatagramSocket", содержит четыре сценария и подразумевает их последовательный запуск на локальном хосте: сначала для создания прослушивателя (для приема сообщения от клиента, Сценарий 1), затем – для создания StreamSocket (Сценарий 2) и отправки сообщения (Сценарий 3), и, в итоге, для закрытия сокета (Сценарий 4). При работе с потоковыми данными, приложение реализует пользовательский протокол для отображения данных, как мы увидим.

    Начнем со Сценария 2 (js/startListener.js), здесь показано создание прослушивателя и обработчика событий. Обработка входящего потока данных сложнее, чем работа с датаграммой, так как нам нужно убедиться в том, что все нужные данные прибыли. Этот код показывает хороший шаблон организации ожидания завершения одной осинхронной операции прежде чем функция рекурсивно вызовет себя. Кроме того, обратите внимание на то, как здесь, для удобства, создан DataReader на основе входного потока:

    socketsSample.listener = new Windows.Networking.Sockets.StreamSocketListener(serviceName);
    // Дополните removeEventListener при необходимости socketsSample.listener.addEventListener
    ("connectionreceived", onServerAccept);
    
    socketsSample.listener.bindServiceNameAsync(serviceName).done(function () {
    // ...
    }, onError);
    }
    
    // Эта функция должна быть реальной; она циклически вызывает сама себя с вызовом
    // acceptAsync в самом конце.
    function onServerAccept(eventArgument) {
    socketsSample.serverSocket = eventArgument.socket;
    socketsSample.serverReader =
    new Windows.Storage.Streams.DataReader(socketsSample.serverSocket.inputStream);
    startServerRead();
    }
    
    // Протокол, используемый здесь, прост: четырехбайтовое с 'сетевым порядком данных' (big-endian) целое число
    // которое сообщает о длине строки, и затем строка указанной длины. Мы ожидаем 4 байта,
    // читаем значение количества, и затем ожидаем указанное количество байтов, после чего отображаем их.
    function startServerRead() {
    socketsSample.serverReader.loadAsync(4).done(function (sizeBytesRead) {
    // Убеждаемся, что прочитаны 4 байта.
    if (sizeBytesRead !== 4) { /* [Show message] */ }
    
    // Читаем 4-х байтовое число и затем читаем заданное количество байтов.
    var count = socketsSample.serverReader.readInt32();
    return socketsSample.serverReader.loadAsync(count).then(function (stringBytesRead) {
    // Убеждаемся, что прочитана вся строка.
    if (stringBytesRead !== count) { /* [Show message] */ }
    
    // Читаем строку.
    var string = socketsSample.serverReader.readString(count);
    socketsSample.displayOutput("Server read: " + string);
    
    // Начинаем чтение заново для большего количества байт. Мы можем просто вызвать startServerRead() но в
    // случае синхронного завершения последовательных операций чтения мы начинаем заполнять стек
    // что может привести к краху приложения. Мы использует WinJS.Promise.timeout() для вызова этой функции
    // после разворачивания стека для текущей операции.
    WinJS.Promise.timeout().done(function () { return startServerRead(); });
    }); // Конец функции "чтения до конца строки" function.
    }, onError);
    }

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

    В Сценарии 2, в стороне взаимоотношений, отправляющих данные, всё устроено просто: создаётся StreamSocket и вызывается connectAsync (js/connectToListener.js; обратите внимание на то, что onError снова использует StreamSocketError.getStatus):

    socketsSample.clientSocket = new Windows.Networking.Sockets.StreamSocket();
    socketsSample.clientSocket.connectAsync(hostName, serviceName).done(function () {
    // ...
    }, onError);
    Отправка данных в Сценарии 3 использует возможности DataWriter, 
    ]построенного на основе выходного потока сокета (js/sendData.js):
    
    var writer = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
    var string = "Hello World";
    var len = writer.measureString(string); // Получает длину UTF-8-строки
    writer.writeInt32(len);
    writer.writeString(string);
    
    writer.storeAsync().done(function () {
    writer.detachStream();
    }, onError);

    Закрытие сокета в Сценарии 4, это, снова, обычный вызов StreamSocket.close.

    Как и в случае с примером "DatagramSocket", установка точек останова внутри openClient (js/connectTo-Listener.js), onServerAccept (js/startListener.js), и sendHello (js/sendData.js) позволит вам увидеть, что происхиодит на каждом из шагов вышеописанного процесса.

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

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

    Как и в случае с обычными сокетами, веб-сокеты в WinRT поддерживают и передачу отдельных пакетов данных, и потоковую передачу: класс MessageWebSocket (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.messagewebsocket.aspx) предназначен для передачи отдельных пакетов, как и в случае с сокетами датаграмм (однако, он использует TCP, а не UDP), и ) предоставляющий средства потоковой передачи данных через сокеты. Оба класса очень похожи на соответствующие им классы 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 В данном случае выходные данные примера отражают информацию, известную приложению и ничего от самого сервиса.

    (рис 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();
      }

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

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

    (рис 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();
      }

    Как и в случае с обычными сокетами, вы можете осуществить дополнительную настройку ). Похожим образом, вы можете настроить безопасное/зашифрованное соединение с использованием схемы URI wss:// вместо ws:// которая использована в примере. Обратитесь к материалу "Обеспечение безопасности подключений )

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

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

  • Пример "ControlChannelTrigger StreamSocket" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-TCP-20c56711)
  • Пример "ControlChannelTrigger XmlHttpRequest" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-6aedf1bc)
  • Пример "ControlChannelTrigger StreamWebSocket" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-91f6bed8)
  • Пример ControlChannelTrigger HTTP client sample" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-HTTP-9d7a6b3d)
  • Неоконченные дела (или некоторые примеры для разбора)

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

    Пример Описание (из Центра разработчиков Windows)
    "Проверка того, является ли текущий сеанс работы удаленным" (http://code.msdn.microsoft.com/windowsapps/Check-if-current-session-7cd31c4e) Пример демонстрирует использование API Windows.System.RemoteDesktop (http://msdn.microsoft.com/library/windows/apps/Hh770630). В частности, данный пример показывает как использовать свойство ) для определения того, является ли текущий сеанс работы удаленным.
    "Приложение 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 рабочего пространства соединения ) Показывает, как использовать объект 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) модели и двоичного содержимого.
  • Страницы:

    Когда мы впервые столкнулись с выполнением запросов XmlHttpRequests с помощью WinJS.XHR в Главе 3 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", мы запрашивали RSS-ленту из Блога для разработчиков приложений для Windows 8, пользуясь URI http://blogs.msdn.com/b/windowsappdev/rss.aspx . Затем мы узнали, что WinJS.xhr возвращает promise-объект, результат которого содержит свойство responseXML , которое имеет тип DomParser, с его помощью можно обойти структуру DOM и так далее.

    Работа с синдицированными веб-каналами, наподобие вышеупомянутой, полностью поддерживается приложениями для Магазина Windows. На самом деле, материал "Доступ к сводному содержимому и управление им" (http://msdn.microsoft.com/ru-ru/library/windows/apps/hh452973.aspx) описывает именно этот процесс, компоненты которого показаны в примере "Интеграция содержимого и элементов управления веб-сервисов" (http://code.msdn.microsoft.com/windowsapps/Mashup-Sample-10689f5b).

    Тем не менее, WinRT предлагает дополнительные API для работы с синдицированным содержимым. Один из них, Windows.Web.Syndication, предлагает более структурированный подход для работы с RSS-каналами. Другой, Windows.Web.AtomPub, предоставляет средства для публикации записей каналов и управления ими. И то и другое предоставлены в WinRT для языков, у которых нет средств для достижения тех же целей, но, как у разработчика, работающего с JavaScript, у вас есть выбор.

    Чтение RSS-каналов

    Основной класс в Windows.Web.Syndication (http://msdn.microsoft.com/library/windows/apps/br244529.aspx) это ). Для работы с любым каналом сначала создают экземпляр этого класса и задают необходимые свойства. Это следующие свойства: serverCredential (типа PasswordCredential), proxyCredential (еще одно свойство типа PasswordCredential), timeout (в миллисекундах, по умолчанию 30000 или 30 секунд), maxResponseBufferSize (средство для защиты от потенциальных серверов злоумышленников), и bypassCacheOnRetrieve (логическое значение, которое указывает на то, всегда ли нужно получать новые данные с сервера ). Так же можно произвести необходимое количество вызовов его метода setRequestHeader (передавая имя и значение) для настройки заголовка XmlHttpRequest.

    Последний шаг заключается в вызове метода ), который получает RSS-канал блога "Создание Windows 8" (http://blogs.msdn.com/b/b8/rss.aspx):

     uri = new Windows.Foundation.Uri("http://blogs.msdn.com/b/b8/rss.aspx");
    
       var client = new Windows.Web.Syndication.SyndicationClient();
       client.bypassCacheOnRetrieve = true;
       client.setRequestHeader("User-Agent",
       "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
    
        client.retrieveFeedAsync(uri).done(function (feed) {
        // feed это объект SyndicationFeed

    Результат выполнения ), то есть, SyndicationClient это то, что вы использовали для взаимодействия с свервисом, и когд вы получаете канал, вы получаете объект, с помощью которого вы можете затем обработать полученные данные. Если вы взглянете на SyndicationFeed , воспользовавшись вышеприведенной ссылкой, вы увидите, что он наполнен свойствами, которые представляют собой все части канала, такие, как authors, categories, items, title, и так далее. Некоторые из них представлены другими классами в Windows.Web.Syndication, или их коллекциями, когда более простых типов недостаточно: SyndicationAttribute, SyndicationCategory, SyndicationContent, SyndicationGenerator, SyndicationItem, SyndicationLink, SyndicationNode, SyndicationPerson, и SyndicationText. Оставляю разъяснение подробностей обо всём этом документации.

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

    client.retrieveFeedAsync(uri).done(function (feed) {
      currentFeed = feed;
    
      var title = "(no title)";
    
      // currentFeed.title это объект SyndicationText
      if (currentFeed.title) {
      title = currentFeed.title.text;
      }
      // currentFeed.items это коллекция SyndicationItem (массив)
      currentItemIndex = 0;
      if (currentFeed.items.size>0) {
      displayCurrentItem();
      }
      }
    
      // ...
    
      function displayCurrentItem() {
      // Элемент будет иметь тип SyndicationItem
    
      var item = currentFeed.items[currentItemIndex];
    
      // Отображение номера элемента.
      document.getElementById("scenario1Index").innerText = (currentItemIndex + 1) + " of "
      + currentFeed.items.size;
    
      // Отображение заголовка (item.title это еще один SyndicationText).
      var title = "(no title)";
      if (item.title) {
      title = item.title.text;
      }
      document.getElementById("scenario1ItemTitle").innerText = title;
    
      // Отображение основной ссылки (item.links это коллекция объектов SyndicationLink).
      var link = "";
      if (item.links.size>0) {
      link = item.links[0].uri.absoluteUri;
      }
    
      var scenario1Link = document.getElementById("scenario1Link");
      scenario1Link.innerText = link;
      scenario1Link.href = link;
    
      // Отображение тела в виде HTML (item.content это объект SyndicationContent, item.summary это
      // объект SyndicationText object).
      var content = "(no content)";
      if (item.content) {
      content = item.content.text;
      }
      else if (item.summary) {
      content = item.summary.text;
      }
      document.getElementById("scenario1WebView").innerHTML = window.toStaticHTML(content);
    
      // Отображение расширений элемента. Коллекция elementExtensions содержит дополнительные
      // дочерние элементы в текущем элементе, которые не принадлежат станартам Atom или RSS
      // (например, элементы расширения Dublin Core). Создавая их массив, мы можем создать
      // WinJS.Binding.List, который можно легко вывести в ListView.
      var bindableNodes = [];
      for (var i = 0; i<item.elementExtensions.size; i++) {
    var bindableNode = {
    nodeName: item.elementExtensions[i].nodeName, nodeNamespace: 
    item.elementExtensions[i].nodeNamespace, nodeValue: 
    item.elementExtensions[i].nodeValue,
    };
    bindableNodes.push(bindableNode);
    }
    var dataList = new WinJS.Binding.List(bindableNodes);
    var listView = document.getElementById("extensionsListView").winControl; 
    WinJS.UI.setOptions(listView, { itemDataSource: dataList.dataSource });
    }

    Видимо, это очевидно, что API, лежащие в основе всего этого, скорее всего, просто используют API XmlDocument для получения этих свойств канала. На самом деле его getXmlDocument возвращает этот XmlDocument, если вам понадобится доступ к нему в собственных целях.

    Так же вы можете создать объект SyndicationFeed на основе XML для канала, который у вас уже есть. Например, если вы получили содержимое канала с использованием WinJS.xhr, вы можете создать новый объект SyndicationFeed и вызвать его метод load со свойством XHR responseXML. Затем вы можете работать с каналом с помощью иерархии класса. При использовании API Windows.Web.AtomPub для управления каналом, вы так же создаете новый или обновленный элемент SyndicationItem для передачи по каналам связи, устанавливаете его значения посредством других объектов в его иерархии. Скоро мы это увидим.

    Еще одно примечание: если retrieveFeedAsync выдает исключение, которое можно получить с помощью обработчика ошибок, который вы предоставили в метод done соответствующего promise-объекта, вы можете преобразовать код ошибки в значение SyndicationErrorStatus. Вот, как это используется в обработчике ошибок примера:

           
    function onError(err) {
    // Сранивает номер ошибки со значением SyndicationErrorStatus. Используйте
    // Windows.Web.WebErrorStatus.getStatus() для получения кодов статусов HTTP - ошибок.
    var errorStatus = Windows.Web.Syndication.SyndicationError.getStatus(err.number);
    if (errorStatus === Windows.Web.Syndication.SyndicationErrorStatus.invalidXml) {
    displayLog("An invalid XML exception was thrown. Please make sure to use a URI that"
    + "points to a RSS or Atom feed.");
    }
    }

    Использование AtomPub

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

    API для этого можно найти в Windows.Web.AtomPub (http://msdn.microsoft.com/library/windows/apps/windows.web.atompub.aspx), это показано в примере "AtomPub" (http://code.msdn.microsoft.com/windowsapps/AtomPub-sample-c1fcdc8e). Главный класс здесь – это ), который инкапсулирует методы, необходимые для реализации протокола AtomPub. У него есть методы наподобие createResourceAsync, retrieveResourceAsync, updateResourceAsync, и deleteResourceAsync для работы с записями, где каждый ресурс идентифицируется с помощью URI и объекта SyndicationItem, при необходимости. Мультимедиа-ресурсами для записей управляют с помощью createMediaResourceAsync и похожим образом названных методов,где ресурс представляется в виде объекта IInputStream.

    У объекта AtomPubClient так же имеются методы retrieveFeedAsync и setRequestHeader, которые выполняют те же функции, что и методы SyndicationClient с такими же именами, а так же есть несколько похожих свойств, наподобие serverCredential, timeout, и bypassCacheOnRetrieve. Еще один метод, retrieveServiceDocumentAsync, предоставляет документацию рабочего пространства/сервиса для канала (в форме объекта Windows.Web.AtomPub.ServiceDocument).

    Снова, пример "AtomPub" ( http://code.msdn.microsoft.com/windowsapps/AtomPub-sample-c1fcdc8e). показывает различные операции: получение данных (Сценарий 1), создание (Сценарий 2), удаление (Сценарий 3) и обновление (Сценарий 4). Вот как, для начала, создаётся объект AtomPubClient (смотрите js/common.js), наличие учетных данных предполагается:

      function createClient() {
      client = new Windows.Web.AtomPub.AtomPubClient();
      client.bypassCacheOnRetrieve = true;
      var credential = new Windows.Security.Credentials.PasswordCredential();
      credential.userName = document.getElementById("userNameField").value;
      credential.password = document.getElementById("passwordField").value;
      client.serverCredential = credential;
      }
      Обновление записи (js/update.js) выглядит так, где обновление представлено вновь созданным объектом SyndicationItem:
      function getCurrentItem() {
      if (currentFeed) {
      return currentFeed.items[currentItemIndex];
      }
      return null;
      }
      var resourceUri = new Windows.Foundation.Uri( /* service address */ );
      createClient();
    
      var currentItem = getCurrentItem();
    
      if (!currentItem) {
      return;
      }
    
      // Обновление элемента
      var updatedItem = new Windows.Web.Syndication.SyndicationItem();
      var title = document.getElementById("titleField").value;
      updatedItem.title = new Windows.Web.Syndication.SyndicationText(title,
      Windows.Web.Syndication.SyndicationTextType.text);
      var content = document.getElementById("bodyField").value;
      updatedItem.content = new Windows.Web.Syndication.SyndicationContent(content,
      Windows.Web.Syndication.SyndicationTextType.html);
    
      client.updateResourceAsync(currentItem.editUri, updatedItem).done(function () {
      displayStatus("Updating item completed.");
      }, onError);

    Обработка ошибок в данном случае работает с классом ) (смотрите js/common.js):

    function onError(err) {
      displayError(err);
    
      // Сравнивает номер ошибки со значением WebErrorStatus, для того, чтобы иметь дело с конкретной ошибкой.
      var errorStatus = Windows.Web.WebError.getStatus(err.number);
      if (errorStatus === Windows.Web.WebErrorStatus.unauthorized) {
      displayLog("Wrong username or password!");
      }
      }

    Сокеты

    Сокеты – это основа сетевого транспорта. В отличие от HTTP-запросов, где клиент отправляет запрос серверу, а сервер отвечает ему, то есть – от отдельного сеанса взаимодействия, сокеты – это соединение между клиентским и серверным IP-портами, так что каждый может в любое время отправлять другому данные по этому каналу. Очевидно, мы видели подобный механизм раньше, а именно, когда использовали Windows Push Notification Service (WNS). WNS, однако, ограничен уведомления и специально создан для отправки обновлений плиток или уведомлений для приложений, которые не исполняются. Сокеты, с другой стороны, созданы для обмена данными между сервером и работающим клиентом.

    Сокеты обычно используют, когда нет API более высокого уровня или других абстрактных механизмов, подходящих для конкретного сценария, когда используются пользовательские протоколы, когда нужна возможность двусторонней связи, или когда важно снизить нагрузку на сеть при каждом обмене данными. Если рассмотреть HTTP, это протокол, который построен на низкоуровневых сокетах. Отдельный HTTP-запрос обычно включает в себя заголовки и множество других данных, помимо небольшого объема полезного содержимого, поэтому это – неэффективный сетевой транспорт, если нужно передать множество небольших фрагментов информации. Лучше – подключиться напрямую к серверу и передавать данные с минимальным количеством служебной информации. VoIP – другой пример, где хорошо работают сокеты, как и широковещательные сценарии наподобие многопользовательских игр. В последних один из компьютеров игроков выполняет роль сервера в локальной подсети, он может передавать сообщения всем остальным игрокам, и наоборот, опять же, с минимальной избыточностью.

    В мире сокетов обмен данными может происходить двумя путями: в виде отдельных пакетов или сообщений (напоминающих ведра с водой), или в виде непрерывного потока (как вода, текущая по трубе). Их называют сокетами датаграмм и сокетами потоков, соответственно, и то и другое поддерживается посредством API WinRT. WinRT так же поддерживает обе формы обмена данными посредством проткола WebSocket, технологии, изначально созданной для веб-браузеров и веб-серверов, которая стала весьма интересной для применения в приложениях, для общих целей. Все применимые классы находятся в API Windows.Networking.Sockets (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.aspx), как мы увидим в следующих разделов. Обратите на это внимание, так как имеется некоторое наложение между разными типами сокетов, эти разделы нужно читать по порядку, так как я не собираюсь повторяться!

    Сокеты датаграмм

    На языке сокетов ведро с водой называется датаграммой. Это пакет данных, отправленных с одного конца сокета на другой – даже без наличия соответствующего соединения. В соответствии со стандартом User Datagram Protocol (UDP). UDP, как я обобщаю здесь из его описания в Википедии (http://ru.wikipedia.org/wiki/UDP), это простой, не предусматривающий сохранение состояния, двунаправленный протокол, ориентированный на отдельные транзакции. Он подразумевает минимальную избыточность и отсутствие задержек повторной передачи данных, и поэтому он не может гарантировать, что датаграмма будет доставлена. Таким образом он используется тогда, когда в проверке ошибок и коррекции нет необходимости, или это выполняется самим приложением, а не на уровне сетевого интерфейса. Например, сценарий использования VoIP позволяет просто терять пакеты, если они не могут быть доставлены, вместо того, чтобы заставлять все остальные механизмы ждать задержавшегося пакета. В результате качество звука может страдать, но говорящие не начнут заикаться или разговаривать как пришельцы из другой галактики. Коротко говоря, UDP может быть ненадежным, но он минимизирует задержки передачи данных. Протоколы более высокого уровня, наподобие Real-time Transport Protocol (RTP) и Real Time Streaming Protocol (RTSP) построены на базе UDP.

    Приложения для Магазина Windows работают с этим средством передачи данных, либо в роли клиента, либо как сервер, используя класс ), объект, который нужно инициализировать с использованием оператора new для настройки соединения и ожидания поступления сообщений:

    var listener = new Windows.Networking.Sockets.DatagramSocket();

    С каждой стороны соединения, следующий шаг – это прослушивание события этого объекта ):

         // Событие из WinRT: помните о вызове removeEventListener при необходимости
          listener.addEventListener("messagereceived", onMessageReceived);

    Когда данные прибывают, обработчик получает то, чего ждет – объект DatagramSocketMessageReceivedEventArgs ( http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.datagramsocketmessagereceivedeventargs.aspx) (и не выговоришь). Он содержит свойства ), которые содержат IP-адрес, отображаемое имя и некоторые другие данные. Смотрите раздел "Информация о сети (Реестр сетевых объектов)" выше в этой лекции чтобы узнать подробности. Аргументы события, так же, содержат строку ), который возвращает IInputStream с помощью которого можно прочесть последовательно расположенные байты данных.

    Другой – ), возвращает объект ), высокоуровневую абстракцию, построенную на основе IInputStream, которая помогает считывать конкретные типы данных непосредственно. Очевидно, если вам известна структура данных, которую вы ожидаете получить в сообщении, использование DataReader освободит вас от необходимости самостоятельно выполнять преобразование типов данных.

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

  • connectAsync Запускает операцию соединения с использованием заданного объекта HostName и имени службы (или строкового представления UDP-порта) удаленного сетевого пункта назначения. Этот метод используется для создания одностороннего соединения клиента с сервером.
  • Другая форма ), который задаёт имя узла и службы и для локальной и для удалённой конечных точек сетевого подключения. Он используется для создания двустороннего соединения клиента и сервера, в то время как локальная конечная точка означает вызов bindEndpointAsync, как показано ниже.
  • bindEndpointAsync Используется для одностороннего серверного соединения, то есть – только для прослушивания, но не для отправки данных в сокет – этот метод просто привязывает локальную конечную точку, заданную в HostName и имени службы или порта. Привязка службы может быть реализована с помощью bindServiceNameAsync.
  • joinMulticastGroup Принимает HostName, присоединяет объект сокета датаграмм к группе многоадресной рассылки.
  • close Прекращает соединение и прерывает все ожидающие операции.
  • Совет. Для того, чтобы открыть сокет на порту локального хоста для целей отладки, воспользуйтесь connectAsync следующим образом:

      var socket = new Windows.Networking.Sockets.DatagramSocket();
      socket.connectAsync(new Windows.Networking.Sockets.DatagramSocket("localhost",
      "12345", Windows.Networking.Sockets.SocketProtectionLevel.plainSocket)
      .done(function () {
      // ...
      }, onError);

    Обратите внимание на то, что так как каждый сокет может быть подключен к любому количеству конечных точек, вы можете вызывать метод connectAsync много раз, присоединяться к множеству групп многоадресной рассылки и привязывать множество локальных конечных точек с помощью bindEndpointAsync и bindServiceNameAsync. Метод close, учтите, закрывает сразу всё!

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

    Управляющие данные могут быть заданы с помощью свойства DatagramSocket.control, которое является объектом

    ) со свойствами ) и ).

    Всё это, конечно, лишь предварительная подготовка к передаче данных по соединению сокета. Это выполняется с помощью свойства ) типа IOutputStream (http://msdn.microsoft.com/library/windows/apps/windows.storage.streams.ioutputstream.aspx), куда вы записываете любые необходимые даные с использованием его методов writeAsync и flushAsync. Благодаря этому данные можно отправить по любому из соединений сокета. Так же можно использовать один из вариантов getOutputStreamAsync для задания конкретных параметров EndpointPair или HostName/порта на которые нужно отправить данные. Результат каждой из этих асинхронных операций – это, снова, IOutputStream. И во всех случаях вы можете создавать высокоуровневый объект DataWriter на основе данного потока:

        var dataWriter = new Windows.Storage.Streams.DataWriter(socket.outputStream)

    Вот, как это всё показано в примере "DatagramSocket" ( http://code.msdn.microsoft.com/windowsapps/DatagramSocket-sample-76a7d82b), небольшом приложении, в котором вам нужно последовательно запустить каждый из сценариев. Сценарий 1, в первую очередь, устанавливает серверный прослушиватель на локальный хост, используя номер порта 22112 (имя службы) по умолчанию. Для того чтобы это сделать, он создает сокеты, добавляет прослушиватель и вызывает bindServiceNameAsync (js/startListener.js):

    socketsSample.listener = new Windows.Networking.Sockets.DatagramSocket();
      // Напоминание: вызовите при необходимости removeEventListener; это может быть обычной процедуре при работе с сокетами,
      // которая сопровождает жизненный цикл приложения.
      socketsSample.listener.addEventListener("messagereceived", onServerMessageReceived);
    
      socketsSample.listener.bindServiceNameAsync(serviceName).done(function () {
      // ...
      }, onError);

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

      function onServerMessageReceived(eventArgument) {
      // [Код здесь проверяет, есть ли уже у нас выходной поток]
    
      socketsSample.listener.getOutputStreamAsync(eventArgument.remoteAddress,
      eventArgument.remotePort).done(function (outputStream) {
      // [Сохраняем выходной поток с некоторыми другими данными, опущено]
      socketsSample.listenerOutputStream = outputStream;
      }
    
      // Это вспомогательная функция
      echoMessage(socketsSample.listenerOutputStream, eventArgument);
      });
      }
      // eventArgument здесь - это DatagramSocketMessageReceivedEventArgs с методом getDataReader method
      function echoMessage(outputStream, eventArgument) {
      // [некоторый код для вывода информации опущен]
    
      // Получаем поток сообщения из DataReader и отправляем его в выходной поток
      outputStream.writeAsync(eventArgument.getDataReader().detachBuffer()).done(function () {
      // Ничего не делаем – клиент выведет сообщение при получении данных.
      });
      }

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

    Сценарий 2 устанавливает прослушиватель на локальный хост, на тот же порт. С этой стороны, мы так же создаем DatagramSocket и настраиваем прослушиватель для messagereceived. Эти сообщения, как то, что записывается в выходной поток на серверной стороне, как мы только что видели – принимаются в обработчике события, код которого приведен ниже (js/connectToListener.js), который использует DataReader для извлечения и отображения сообщения:

      function onMessageReceived(eventArgument) {
      try {
      var messageLength = eventArgument.getDataReader().unconsumedBufferLength;
      var message = eventArgument.getDataReader().readString(messageLength);
       socketsSample.displayStatus("Client: receive message from server \"" + message + "\"");
      } catch (exception) {
      status = Windows.Networking.Sockets.SocketError.getStatus(exception.number);
      // [Отображение подробностей об ошибке]
      }
      }

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

    Даже после того, как всё вышеописанное сделано, ничего пока не происходит, так как мы еще не отправили данные! Переход к Сценарию 3 и нажатие его кнопки Send ‘Hello’ Now выполняет действия от имени клиента:

          // [Это происходит после проверки действительности сокета]
          socketsSample.clientDataWriter =
          new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
    
          var string = "Hello World";
          socketsSample.clientDataWriter.writeString(string);
    
          socketsSample.clientDataWriter.storeAsync().done(function () {
          socketsSample.displayStatus("Client sent: " + string + ".");
          }, onError);

    Вызов DataWriter.storeAsync это то, что осуществляет запись в поток в сокете. Если вы установите здесь точку останова и в обоих обработчиках события messagereceived, вы увидите, что storeAsync генерирует сообщение для сервера, когда точка останова сработает в onServerMessageReceived в js/startListener.js. Затем будет осуществлена запись обратно в сокет, что вызовет остановку в onMessageReceived в js/connectToListener.js, что приведет к выводу сообщения. (И, для того, чтобы завершить этот процесс, Сценарий 4 предоставляет кнопку для вызова метода сокета close.)

    Пример выполняет всё с одним и тем же приложением на локальном хосте, что даёт простую возможность увидеть, как всё это работает. Обычно, конечно, сервер будет исполняться на другом компьютере, но шаги по настройке прослушивателей в подобном случае выглядят так же. Как отмечено в Главе 2, соединения локального хоста работают лишь на компьютере с лицензией разработчика и не будут работать для приложений, полученных из Магазина Windows.

    Сокеты потока

    В отличие от сокетов датаграмм, потоковая передача данных через сокеты Transmission Control Protocol(TCP). Отличительная черта TCP заключается в точной и надёжной доставке – он гарантирует, что принятые байты соответствуют отправленным: когда пакеты передаются по сети, TCP пытается повторять их передачу, если что-то пошло не так. Именно поэтому он является частью TCP/IP, что дает нам Всемирную паутину, электронную почту, передачу файлов и многое другое. HTTP, SMTP, и Session Initiation Protocol (SIP) так же построены на основе TCP. Во всех случаях, клиенты и сервера видят лишь надёжный поток данных, путешествующих от одного конца соединения к другому.

    В отличие от сокетов датаграмм, для которых имеется лишь один класс WinRT, для обеих сторон соединения, сокеты потока различаются сильнее, для соответствия уникальным нуждам клиентской и серверной ролей. На клиентской стороне это ); на серверной – StreamSocketListener (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.streamsocketlistener.aspx).

    Начнем с последнего, объект StreamSocketListener очень похож на DatagramSocket, который мы только что рассмотрели в предыдущем разделе, он имеет следующие методы, свойства и события:

  • ), содержащий строку localPort.
  • ) со свойством qualityOfService.
  • ) содержат единственное свойство, socket. Это StreamSocket для клиента, в котором есть свойство outputStream , из которого прослушиватель может получить поток данных.
  • bindEndpointAsync и bindServiceNameAsync Привязывают прослушиватель к HostName и имени службы, или осуществляют привязку лишь к имени сервиса.
  • close Прекращает соединение и прерывает все ожидающие операции.
  • Со стороны клиента, )) и )) и вездесущему методу close, мы обнаруживаем несколько вполне обычных и один необычный:

  • ), который может принимать значения plainSocket, ssl, или sslAllowNullEncryption. Это, другими словами, четыре варианта данного метода.
  • inputStream Обект IInputStream который принимает данные через соединению.
  • outputStream Объект IOutputStream в который осуществляется запись данных.
  • ) Обновляет соединение типа plainSocket (созданное с помощью connectAsync) для использования SSL, что задаётся либо SocketProtectionLevel.ssl либо sslAllowNullEncryption. Эти методы так же требуют HostName, который подтверждает соединение.
  • Для того, чтобы узнать больше об использовании SSL, смотрите материал "Обеспечение безопасности подключений через сокеты с помощью протокола TLS/SSL" (http://msdn.microsoft.com/library/windows/apps/hh780595.aspx).

    В любом случае, вы можете видеть, что для односторонней связи с использованием TCP приложение создает либо StreamSocket либо StreamSocketListener, в зависимости от его роли. Для двусторонней связи приложению нужно и то и другое.

    Пример "StreamSocket" (http://code.msdn.microsoft.com/windowsapps/StreamSocket-Sample-8c573931), как и пример "DatagramSocket", содержит четыре сценария и подразумевает их последовательный запуск на локальном хосте: сначала для создания прослушивателя (для приема сообщения от клиента, Сценарий 1), затем – для создания StreamSocket (Сценарий 2) и отправки сообщения (Сценарий 3), и, в итоге, для закрытия сокета (Сценарий 4). При работе с потоковыми данными, приложение реализует пользовательский протокол для отображения данных, как мы увидим.

    Начнем со Сценария 2 (js/startListener.js), здесь показано создание прослушивателя и обработчика событий. Обработка входящего потока данных сложнее, чем работа с датаграммой, так как нам нужно убедиться в том, что все нужные данные прибыли. Этот код показывает хороший шаблон организации ожидания завершения одной осинхронной операции прежде чем функция рекурсивно вызовет себя. Кроме того, обратите внимание на то, как здесь, для удобства, создан DataReader на основе входного потока:

    socketsSample.listener = new Windows.Networking.Sockets.StreamSocketListener(serviceName);
    // Дополните removeEventListener при необходимости socketsSample.listener.addEventListener
    ("connectionreceived", onServerAccept);
    
    socketsSample.listener.bindServiceNameAsync(serviceName).done(function () {
    // ...
    }, onError);
    }
    
    // Эта функция должна быть реальной; она циклически вызывает сама себя с вызовом
    // acceptAsync в самом конце.
    function onServerAccept(eventArgument) {
    socketsSample.serverSocket = eventArgument.socket;
    socketsSample.serverReader =
    new Windows.Storage.Streams.DataReader(socketsSample.serverSocket.inputStream);
    startServerRead();
    }
    
    // Протокол, используемый здесь, прост: четырехбайтовое с 'сетевым порядком данных' (big-endian) целое число
    // которое сообщает о длине строки, и затем строка указанной длины. Мы ожидаем 4 байта,
    // читаем значение количества, и затем ожидаем указанное количество байтов, после чего отображаем их.
    function startServerRead() {
    socketsSample.serverReader.loadAsync(4).done(function (sizeBytesRead) {
    // Убеждаемся, что прочитаны 4 байта.
    if (sizeBytesRead !== 4) { /* [Show message] */ }
    
    // Читаем 4-х байтовое число и затем читаем заданное количество байтов.
    var count = socketsSample.serverReader.readInt32();
    return socketsSample.serverReader.loadAsync(count).then(function (stringBytesRead) {
    // Убеждаемся, что прочитана вся строка.
    if (stringBytesRead !== count) { /* [Show message] */ }
    
    // Читаем строку.
    var string = socketsSample.serverReader.readString(count);
    socketsSample.displayOutput("Server read: " + string);
    
    // Начинаем чтение заново для большего количества байт. Мы можем просто вызвать startServerRead() но в
    // случае синхронного завершения последовательных операций чтения мы начинаем заполнять стек
    // что может привести к краху приложения. Мы использует WinJS.Promise.timeout() для вызова этой функции
    // после разворачивания стека для текущей операции.
    WinJS.Promise.timeout().done(function () { return startServerRead(); });
    }); // Конец функции "чтения до конца строки" function.
    }, onError);
    }

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

    В Сценарии 2, в стороне взаимоотношений, отправляющих данные, всё устроено просто: создаётся StreamSocket и вызывается connectAsync (js/connectToListener.js; обратите внимание на то, что onError снова использует StreamSocketError.getStatus):

    socketsSample.clientSocket = new Windows.Networking.Sockets.StreamSocket();
    socketsSample.clientSocket.connectAsync(hostName, serviceName).done(function () {
    // ...
    }, onError);
    Отправка данных в Сценарии 3 использует возможности DataWriter, 
    ]построенного на основе выходного потока сокета (js/sendData.js):
    
    var writer = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
    var string = "Hello World";
    var len = writer.measureString(string); // Получает длину UTF-8-строки
    writer.writeInt32(len);
    writer.writeString(string);
    
    writer.storeAsync().done(function () {
    writer.detachStream();
    }, onError);

    Закрытие сокета в Сценарии 4, это, снова, обычный вызов StreamSocket.close.

    Как и в случае с примером "DatagramSocket", установка точек останова внутри openClient (js/connectTo-Listener.js), onServerAccept (js/startListener.js), и sendHello (js/sendData.js) позволит вам увидеть, что происхиодит на каждом из шагов вышеописанного процесса.

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

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

    Как и в случае с обычными сокетами, веб-сокеты в WinRT поддерживают и передачу отдельных пакетов данных, и потоковую передачу: класс MessageWebSocket (http://msdn.microsoft.com/library/windows/apps/windows.networking.sockets.messagewebsocket.aspx) предназначен для передачи отдельных пакетов, как и в случае с сокетами датаграмм (однако, он использует TCP, а не UDP), и ) предоставляющий средства потоковой передачи данных через сокеты. Оба класса очень похожи на соответствующие им классы 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 В данном случае выходные данные примера отражают информацию, известную приложению и ничего от самого сервиса.

    (рис 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();
      }

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

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

    (рис 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();
      }

    Как и в случае с обычными сокетами, вы можете осуществить дополнительную настройку ). Похожим образом, вы можете настроить безопасное/зашифрованное соединение с использованием схемы URI wss:// вместо ws:// которая использована в примере. Обратитесь к материалу "Обеспечение безопасности подключений )

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

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

  • Пример "ControlChannelTrigger StreamSocket" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-TCP-20c56711)
  • Пример "ControlChannelTrigger XmlHttpRequest" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-6aedf1bc)
  • Пример "ControlChannelTrigger StreamWebSocket" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-91f6bed8)
  • Пример ControlChannelTrigger HTTP client sample" (http://code.msdn.microsoft.com/windowsapps/ControlChannelTrigger-HTTP-9d7a6b3d)
  • Неоконченные дела (или некоторые примеры для разбора)

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

    Пример Описание (из Центра разработчиков Windows)
    "Проверка того, является ли текущий сеанс работы удаленным" (http://code.msdn.microsoft.com/windowsapps/Check-if-current-session-7cd31c4e) Пример демонстрирует использование API Windows.System.RemoteDesktop (http://msdn.microsoft.com/library/windows/apps/Hh770630). В частности, данный пример показывает как использовать свойство ) для определения того, является ли текущий сеанс работы удаленным.
    "Приложение 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 рабочего пространства соединения ) Показывает, как использовать объект 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) модели и двоичного содержимого.
  • Вернуться к учебному плану