wcf提供了streaming方式后,一直有个小问题,找不到合适的stream载体,如果能用上文件流什么的做返回值那是最好不过了,但是更多的情况下,需要返回一个流,但是这个流并没有类似文件之类的真实载体,而且有时候这个流还比较大(如果很小的话,也不需要用到streaming方式了),这时候似乎就有那么点麻烦了。

    首先,我不喜欢用MemoryStream,因为它真实的占用了这么多内存,遇到大数据量的情况下,wcf的streaming方式的威力将大大降低。当然,也可以借助文件,返回文件流来绕开这个问题,或者使用其他的什么现成的流。

    然而,我更倾向于使用类似Circular Buffer的逻辑来处理这个流,这样可以占用很小的内存,来输出很大体积的流,最大限度的体现wcf的streaming模式的优势。

    不过,网上查了几个Circlar Buffer的实现,总感觉不是特别满意。。。无奈之下只能自己动手打造一个:

CircularStream
    public class CircularStream
        : Stream
    {

        #region Fields
        private const int DefaultCapacity = 0x10000;
        private readonly object SyncRoot = new object();
        private bool m_closed;
        private readonly byte[] m_buffer;
        private int m_read;
        private int m_write;
        #endregion

        #region Ctors

        public CircularStream(int capacity)
        {
            m_buffer = new byte[capacity];
        }

        public CircularStream(Action<Stream> action)
            : this(DefaultCapacity)
        {
            ThreadPool.QueueUserWorkItem(_ =>
            {
                try
                {
                    action(this);
                }
                finally
                {
                    this.Dispose();
                }
            });
        }

        #endregion

        #region Overrides

        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 Length
        {
            get { throw new NotSupportedException(); }
        }

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

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

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

        protected override void Dispose(bool disposing)
        {
            lock (SyncRoot)
            {
                m_closed = true;
                Monitor.Pulse(SyncRoot);
            }
            base.Dispose(disposing);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            lock (SyncRoot)
            {
                while (m_read == m_write)//empty
                    if (m_closed)
                        return 0;
                    else
                        Monitor.Wait(SyncRoot, 100true);
                int c;
                if (m_write > m_read)
                    c = m_write - m_read;
                else
                    c = m_buffer.Length - m_read;
                Monitor.Pulse(SyncRoot);
                if (c > count)
                {
                    Buffer.BlockCopy(m_buffer, m_read, buffer, offset, count);
                    m_read += count;
                    return count;
                }
                else
                {
                    Buffer.BlockCopy(m_buffer, m_read, buffer, offset, c);
                    m_read = (m_read + c) % m_buffer.Length;
                    return c;
                }
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            lock (SyncRoot)
            {
                if (m_closed)
                    throw new ObjectDisposedException("CircleBufferStream");
                while (count > 0)
                {
                    while ((m_write + 1) % m_buffer.Length == m_read)// full
                        Monitor.Wait(SyncRoot, 100true);
                    if (m_read > m_write)
                    {
                        int c = Math.Min(m_read - m_write - 1, count);
                        Buffer.BlockCopy(buffer, offset, m_buffer, m_write, c);
                        m_write += c;
                        count -= c;
                        offset += c;
                    }
                    else
                    {
                        int c = m_buffer.Length - m_write;
                        if (m_read == 0)
                            --c;
                        if (c > count)
                        {
                            Buffer.BlockCopy(buffer, offset, m_buffer, m_write, count);
                            m_write += count;
                            count = 0;
                        }
                        else
                        {
                            Buffer.BlockCopy(buffer, offset, m_buffer, m_write, c);
                            m_write = (m_write + c) % m_buffer.Length;
                            count -= c;
                            offset += c;
                        }
                    }
                    Monitor.Pulse(SyncRoot);
                }
            }
        }

        #endregion

        #region Properties

        public int Capacity { get { return m_buffer.Length; } }

        #endregion

    }

    加上配套的契约和实现:

契约
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        Stream GetStream(int value);
    }
实现
    public class Service1 : IService1
    {
        public Stream GetStream(int value)
        {
            return new CircularStream(s =>
                new XStreamingElement("root",
                    from i in Enumerable.Range(1, value)
                    select new XElement("item",
                        new XAttribute("id", i))).Save(s));
        }
    }

    以及调用的服务和客户端:

服务和客户端
    static class Program
    {
        static void Main(string[] args)
        {
            var host = new ServiceHost(typeof(Service1));
            host.Open();
            var binding= new BasicHttpBinding();
            binding.TransferMode=TransferMode.StreamedResponse;
            binding.MaxReceivedMessageSize = int.MaxValue;
            // 如果觉得不过瘾,可以打开循环爽一把
            
//for (int i = 0; i < 100; i++)
            
//{
                new Uri("http://localhost:12123/").InvokeWcfClient<IService1>(binding, c =>
                {
                    var stream = c.GetStream(10000);
                    string line;
                    using (var r = new StreamReader(stream))
                        while (null != (line = r.ReadLine()))
                            Console.WriteLine(line);
                });
            //}
            host.Close();
        }

        public static void InvokeWcfClient<TChannel>(
            this Uri uri, Binding binding, Action<TChannel> action)
            where TChannel : class
        {
            TChannel client = ChannelFactory<TChannel>.CreateChannel(
                binding, new EndpointAddress(uri));
            var co = (ICommunicationObject)client;
            try
            {
                co.Open();
                action(client);
            }
            finally
            {
                if (co.State == CommunicationState.Faulted)
                    co.Abort();
                else
                    co.Close();
            }
        }
    }

    这样就可以完美运行了

posted on 2012-02-28 15:26  Zhenway  阅读(329)  评论(1编辑  收藏  举报