【问题标题】:WCF Streaming mode doesn't seem to be streamingWCF 流模式似乎没有流式传输
【发布时间】:2012-06-29 21:22:10
【问题描述】:

我一直在尝试构建一个 WCF 流式传输示例,仅用于测试目的,但我不能确定它是否真的是流式传输。

示例非常基础:

  1. 服务器返回大型二进制内容(本例为 PDF 文件)
  2. 客户端将大型二进制内容写入文件。

但是,即使我相信我已经正确配置了服务器和客户端以进行流传输,问题似乎仍然存在:

  1. 它似乎实际上不是流传输,因为我遇到了 IOException 消息 The maximum message size quota for incoming messages (65536) has been exceeded
  2. 读取以 1536 字节为增量,即使我将流缓冲区设置为 8192(或任何其他大小)

完整的主机代码在这里:

using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace WcfStreamingHost
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            BasicHttpBinding binding = new BasicHttpBinding();
            binding.TransferMode = TransferMode.Streamed;
            binding.MaxBufferSize = 65536;
            binding.MaxReceivedMessageSize = 65536;
            binding.ReaderQuotas.MaxBytesPerRead = 65536;
            binding.SendTimeout = TimeSpan.FromMinutes(10);

            ServiceHost host = new ServiceHost(typeof (ContentProvider), new Uri("http://charles-m4600:1234/contentprovider"));

            host.Description.Behaviors.Add(new ServiceMetadataBehavior());
            host.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
            host.AddServiceEndpoint(typeof (IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
            host.AddServiceEndpoint(typeof (IContentProvider), binding, "streamed");

            host.Open();

            Console.ReadKey();
        }
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
    public class ContentProvider : IContentProvider
    {
        #region IContentProvider Members

        [OperationBehavior(AutoDisposeParameters = true)]
        public Stream GetFile()
        {
            Stream stream = File.OpenRead("large_file.pdf");

            return stream;
        }

        #endregion
    }

    [ServiceContract]
    public interface IContentProvider
    {
        [OperationContract]
        Stream GetFile();
    }
}

完整的客户端代码在这里:

using System;
using System.IO;
using System.ServiceModel;
using WcfStreamingClient.LocalSvc;

namespace WcfStreamingClient
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            BasicHttpBinding binding = new BasicHttpBinding();
            binding.TransferMode = TransferMode.Streamed;
            binding.MaxBufferSize = 65536;
            binding.MaxReceivedMessageSize = 65536;
            binding.ReaderQuotas.MaxBytesPerRead = 65536;
            binding.ReceiveTimeout = TimeSpan.FromMinutes(10);

            EndpointAddress address = new EndpointAddress("http://charles-m4600:1234/contentprovider/streamed");

            using (ContentProviderClient client = new ContentProviderClient(binding, address))
            {
                using (Stream stream = client.GetFile())
                {
                    FileInfo file = new FileInfo("output.pdf");

                    if (file.Exists)
                    {
                        file.Delete();
                    }

                    using (FileStream fileStream = file.Create())
                    {
                        const int bufferLen = 8192;
                        byte[] buffer = new byte[bufferLen];
                        int count = 0;
                        int total = 0;
                        while ((count = stream.Read(buffer, 0, bufferLen)) > 0)
                        {
                            fileStream.Write(buffer, 0, count);
                            total += count;
                            Console.Out.WriteLine("Read {0} bytes", total);
                        }
                    }
                }
            }
        }
    }
}

我已经阅读了有关此问题的各种其他帖子,但似乎找不到任何线索。

【问题讨论】:

  • 我应该补充一点,如果我将MaxReceivedMessageSize 设置为Int32.MaxValue,我可以让它正常工作。虽然看起来这不再是“流式传输”,但查看 Windows 任务管理器确实确认当我将其设置为 StreamedBuffered 时内存配置文件不同。但是,我仍然很好奇为什么 - 在流模式下 - 我必须将 MaxReceivedMessageSize 设置为 Int32.MaxValue 以及为什么我的读取如此之小。

标签: wcf stream


【解决方案1】:

虽然你的帖子是很久以前的事了,但我偶然发现了它,并认为我会分享我的发现。

始终考虑最大邮件大小配额。无论您是否有流式传输。 MSDN对此非常清楚。可以另外指定最大缓冲区大小。

http://msdn.microsoft.com/en-us/library/ms731078%28v=vs.100%29.aspx

MaxReceivedMessageSize:在传输引发异常之前接收到的消息(包括标头)的最大大小(以字节为单位)。

MaxBufferSize:用于流式传输数据的缓冲区的最大大小(以字节为单位)。如果未设置此传输配额,或者传输未使用流式传输,则配额值与 MaxReceivedMessageSize 配额值和 MaxValue 中的较小者相同。

为什么你总是有 1536 字节的块,我不完全确定。但我认为这是因为 以太网帧 的最大尺寸(巨型帧除外):

http://en.wikipedia.org/wiki/Ethernet_frame

【讨论】:

  • 有趣的发现(而且及时),因为我刚刚编写了流服务的另一个实现,并且遇到了同样的问题,必须设置 MaxReceievedMessageSize。 1536 仍然让我感到困惑,因为 WCF 本身似乎会抽象网络堆栈的较低级别,因此以太网帧大小是无关紧要的(换句话说,在返回缓冲区之前等待接收到足够的帧)。好笔记,不管怎样!
【解决方案2】:

MaxReceivedMessageSize 用于防止 DOS 对传入通道的攻击,MaxBufferSize 控制通道上的消息缓冲区大小。当通道配置为流式传输时,仅缓冲肥皂标头并流式传输主体,流块大小由服务实现控制(在您的情况下为 8Kb ) 和 maxrecievedmessagesize 限制文件+标题的大小。 maxrecieved 消息大小必须等于缓冲模式下文件 MaxBufferSize 的大小。但是在流式传输中 MaxBufferSize 必须很小,并且 MaxRecievedMessafeSize 是预期的文件大小。在流式传输模式下,可以使用 MaxBufferSize 来防止 DOS 攻击。

【讨论】:

  • 这是很好的信息,但我认为它不能回答我的问题。主要是,在流模式下,当我将MaxReceivedMessageSize 设置为与MaxBufferSize 相同的大小并且在我的流读取中,我将我的缓冲区设置为 8192 字节,为什么我一次只读取 1536 以及为什么我会运行遇到MaxReceivedMessageSize 被超出的问题。
  • 我猜是因为底层传输是 http,因为传入的消息大小和它们来的速度可能会有所不同。有时,当您阅读时,您只有很少的块数据可用,有时可以流入非常大的消息。
  • 我在我的机器上的主机和客户端之间本地运行此程序。这是相当一致的。实际上,基于额外的研究,这似乎是 WCF 的一个限制,可以通过设置 connectionBufferSize 配置在 NetTcpBinding 情况下克服,但我想知道基于 HTTP 的绑定是否也可以实现这样的事情?
猜你喜欢
  • 2017-09-20
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多