簡體   English   中英

如何從任務異步返回WCF響應中的流?

[英]How can I return a stream in a WCF response asynchronously from a task?

我有一個類,該類對大量數據進行長時間運行的處理,並將輸出寫入我提供的流中。 我正在嘗試將WCF放在此前面(使用命名管道),但是在弄清楚如何返回流時遇到了麻煩。 到目前為止,我有這樣的事情:

interface IProcessor { Stream GetStream(); }

class Host {
  static void Main(string[] args) {
    using (ServiceHost sh = new ServiceHost(typeof(Processor), new Uri[]{new Uri("net.pipe://localhost")})) {
      var binding = new NetNamedPipeBinding();
      binding.TransferMode = TransferMode.StreamedResponse;
      sh.AddServiceEndpoint(typeof(IProcessor), binding, "foo");
      sh.Open();
      Console.WriteLine("Waiting...");
      Console.ReadLine();
      sh.Close();
    }
  }
}

class Processor : IProcessor {
  Stream GetStream() {
    var SourceProcessor = new SourceProcessor(...);
    var s = new MemoryStream();
    new Task(() => { SourceProcessor.Run(s); }).Start();
    return s;
  }
}

class Client {
  static void Main(string[] args) {
    Console.WriteLine("Starting...");
    var binding = new NetNamedPipeBinding();
    binding.TransferMode = TransferMode.StreamedResponse;
    ChannelFactory<IProcessor> f = new ChannelFactory<IProcessor>(binding, new EndpointAddress("net.pipe://localhost/foo"));
    Console.WriteLine("Creating channel...");
    IProcessor eps = f.CreateChannel();
    Console.WriteLine("Getting stream.");
    Stream s = eps.GetStream();
    StreamReader sr = new StreamReader(s);
    while (!sr.EndOfStream) Console.WriteLine(sr.ReadLine());
    Console.ReadLine();
  }
}

一切都會進行,但當然沒有源數據可以傳遞給客戶端。 我對如何做到這一點感到困惑(也許我做不到),因為我既需要返回流並運行任務,又可能等待任務完成。 如果我不調用任務就直接調用SourceProcessor.Run,​​它會阻塞和緩沖,但是,我不確定如何等到任務完成后再返回流以供客戶端讀取...

問題是,如果WCF調用Read(且調用返回0字節,WCF會認為流“完成” MemoryStream會很樂意這樣做,如果沒有可用數據,它將不阻止讀取。

問題的根源是WCF正在讀取MemoryStream速度比您編寫它的速度快,並認為它已“完成”,解決該問題的方法是,您需要返回阻塞的另一種類型的Stream ,而不是在出現這種情況時返回0沒有可用數據。 .NET沒有內置可實現此目的的東西,您將需要找到一個第三方類或創建自己的類(它可能很簡單,就像從MemoryStream派生並覆蓋Read以阻止讀取,直到出現“ Done”標志為止設置(有關類似行為,請參見BlockingCollection<T>及其CompleteAdding()方法)。

出於樂趣,我將其組合在一起,雖然未經測試,但可以滿足您的需要。

using System;
using System.Collections.Concurrent;
using System.IO;

namespace Example
{
    public class BufferStream : Stream
    {
        public BufferStream()
        {
            _data = new BlockingCollection<byte[]>();
        }


        /// <param name="boundedCapacity">The maximum number of calls to <see cref="Write"/> that can be made without
        /// the buffer being drained.</param>
        public BufferStream(int boundedCapacity)
        {
            _data = new BlockingCollection<byte[]>(boundedCapacity);
        }

        private readonly BlockingCollection<byte[]> _data;
        private byte[] _currentBlock = null;
        private int _currentBlockIndex = 0;

        public int BoundedCapacity { get { return _data.BoundedCapacity; } }

        public int BufferedWrites { get { return _data.Count; } }

        public bool IsAddingCompleted
        {
            get { return _data.IsAddingCompleted; }
        }

        public bool IsCompleted
        {
            get { return _data.IsCompleted; }
        }

        public void CompleteAdding()
        {
            _data.CompleteAdding();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            var localArray = new byte[count];

            //Copy the data in to a new buffer of exactly the count size.
            Array.Copy(buffer, offset, localArray, 0, count);

            _data.Add(localArray);
        }


        public override int Read(byte[] buffer, int offset, int count)
        {
            if (_currentBlock == null || _currentBlockIndex == _currentBlock.Length)
            {
                if (!GetNextBlock()) 
                    return 0;
            }

            int minCount = Math.Min(count, _currentBlock.Length - _currentBlockIndex);

            Array.Copy(_currentBlock, _currentBlockIndex, buffer, offset, minCount);
            _currentBlockIndex += minCount;

            return minCount;
        }

        /// <summary>
        /// Loads the next block in to <see cref="_currentBlock"/>.
        /// </summary>
        /// <returns>True if the next block was retrieved.</returns>
        private bool GetNextBlock()
        {
            if (!_data.TryTake(out _currentBlock))
            {
                //The TryTake failed, the collection is empty.

                //See if we are in the completed state.
                if (_data.IsCompleted)
                {
                    return false;
                }

                //Wait for more data to show up.
                try
                {
                    _currentBlock = _data.Take();
                }
                catch (InvalidOperationException)
                {
                    //If the blocking collection was marked complete while we where waiting Take throws a InvalidOperationException
                    return false;
                }
            }

            _currentBlockIndex = 0;
            return true;
        }

        #region Constant functions

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override void Flush()
        {
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        #endregion
    }
}

這種從MemoryStream派生的優點是,一旦WCF讀取了一個值,它就不再需要保留在內存中(返回Stream而不是byte[]的整個過程)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM