Поделиться через


Пример потоковой передачи каналов

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

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

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

Демонстрация использует итераторы Visual C# (используя конструкцию yield return ключевое слово). Дополнительные сведения об итераторах см. в разделе "Использование итераторов" в MSDN.

Service

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

[ServiceContract]
interface IStreamingFeedService
{
    [WebGet]
    [OperationContract]
    Atom10FeedFormatter StreamedFeed();
}

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

class ItemGenerator
{
    public IEnumerable<SyndicationItem> GenerateItems()
    {
        while (counter.GetCount() < maxItemsRead)
        {
            itemsReturned++;
            yield return CreateNextItem();
        }

    }
    ...
}

Когда реализация службы создает веб-канал, вместо буферизованной коллекции элементов используется вывод метода ItemGenerator.GenerateItems().

public Atom10FeedFormatter StreamedFeed()
{
    SyndicationFeed feed = new SyndicationFeed("Streamed feed", "Feed to test streaming", null);
    //Generate an infinite stream of items. Both the client and the service share
    //a reference to the ItemCounter, which allows the sample to terminate
    //execution after the client has read 10 items from the stream
    ItemGenerator itemGenerator = new ItemGenerator(this.counter, 10);

    feed.Items = itemGenerator.GenerateItems();
    return feed.GetAtom10Formatter();
}

В результате поток элементов никогда не помещается в буфер памяти целиком. Это поведение можно наблюдать, задав точку останова в yield return операторе ItemGenerator.GenerateItems() внутри метода и отметив, что эта точка останова встречается в первый раз после того, как служба вернула результат StreamedFeed() метода.

Клиент

Клиент в этом образце применяет пользовательскую реализацию SyndicationFeedFormatter, которая задерживает воссоздание отдельных элементов в веб-канале вместо их записи в буфер в памяти. Пользовательский экземпляр StreamedAtom10FeedFormatter используется следующим образом.

XmlReader reader = XmlReader.Create("http://localhost:8000/Service/Feeds/StreamedFeed");
StreamedAtom10FeedFormatter formatter = new StreamedAtom10FeedFormatter(counter);

SyndicationFeed feed = formatter.ReadFrom(reader);

Обычно вызов метода ReadFrom(XmlReader) не завершается, пока будет прочитано из сети и записано в буфер в памяти все содержимое веб-канала. Однако класс StreamedAtom10FeedFormatter переопределяет метод ReadItems(XmlReader, SyndicationFeed, Boolean), чтобы вместо буферизованной коллекции он возвращал итератор, как показано в следующем примере кода.

protected override IEnumerable<SyndicationItem> ReadItems(XmlReader reader, SyndicationFeed feed, out bool areAllItemsRead)
{
    areAllItemsRead = false;
    return DelayReadItems(reader, feed);
}

private IEnumerable<SyndicationItem> DelayReadItems(XmlReader reader, SyndicationFeed feed)
{
    while (reader.IsStartElement("entry", "http://www.w3.org/2005/Atom"))
    {
        yield return this.ReadItem(reader, feed);
    }

    reader.ReadEndElement();
}

В результате элемент не будет прочитан из сети, пока клиентское приложение, через которое проходят результаты вызова метода ReadItems(), не будет готово к использованию этого элемента. Это поведение можно наблюдать, задав точку останова в yield return инструкции внутри StreamedAtom10FeedFormatter.DelayReadItems() и замечая, что эта точка останова встречается в первый раз после завершения вызова ReadFrom() .

Ниже показано, как выполнить построение и запуск этого образца. Обратите внимание, что хотя сервер прекращает создание элементов после того, как клиент прочтет 10 элементов, результат работы программы показывает, что клиент прочитывает гораздо больше, чем 10 элементов. Это связано с тем, что используемая сетевая привязка передает данные фрагментами по 4 килобайта (КБ). Таким образом, клиент получает 4 КБ данных элемента раньше, чем он сможет прочитать хотя бы один элемент. Это нормальное поведение (разбиение данных на небольшие фрагменты при их потоковой передаче по протоколу HTTP повышает производительность).

Настройка, сборка и выполнение образца

  1. Убедитесь, что вы выполнили процедуру однократной установки для примеров Windows Communication Foundation.

  2. Чтобы создать выпуск решения на языке C# или Visual Basic .NET, следуйте инструкциям в разделе Building the Windows Communication Foundation Samples.

  3. Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в разделе "Примеры Windows Communication Foundation".

См. также