WCF 流传输模式似乎并不是真正的流传输

4

我一直在尝试构建一个WCF流传输的示例,仅用于测试目的,但我无法确定它是否实际进行了流传输。

这个示例非常基本:

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

然而,问题似乎是尽管我认为我已经正确地配置了服务器和客户端以进行流传输:

  1. 它似乎并不是实际的流传输,因为我遇到了IOException,其消息为已超过传入消息的最大消息大小配额(65536)
  2. 即使我将流缓冲区设置为8192(或任何其他大小),读取也是按1536字节的增量进行的

完整的主机代码在此处:

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任务管理器中查看内存配置文件时,确实会有所不同,当我将其设置为“Streamed”和“Buffered”。但是,我仍然很好奇为什么在流模式下,我必须将“MaxReceivedMessageSize”设置为“Int32.MaxValue”,以及为什么我的读取如此之小。 - Charles Chen
2个回答

4

虽然您的帖子已经有一段时间了,但我偶然发现它,想分享我的发现。

始终考虑最大的消息大小限额,无论你是否具有流传输。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本身会抽象网络栈的较低层,以使以太网帧大小无关紧要(换句话说,在返回缓冲区之前等待接收足够的帧)。 不管怎样,这是很好的笔记! - Charles Chen

2
MaxReceivedMessageSize 用于防止入站通道上的DOS攻击,MaxBufferSize 控制通道上的消息缓冲区大小。当通道配置为仅流式传输时,仅会缓冲 SOAP 头,而正文则会被流式传输。流式块大小由服务实现控制(在您的情况下为8Kb),而maxrecievedmessagesize限制了文件+头的大小。在缓冲模式下,maxrecieved message size 必须等于文件大小,而在流媒体模式下,MaxBufferSize 必须很小,而MaxRecievedMessafeSize 则预期文件大小。在流媒体模式下,MaxBufferSize 可用于防止DOS攻击。

这是很好的信息,但我认为它并没有回答我的问题。主要是,在流模式下,当我将MaxReceivedMessageSize设置为与MaxBufferSize相同的大小,并且在我的流读取中,我将缓冲区设置为8192字节时,为什么我每次只读取1536个字节,并且为什么我会遇到超过MaxReceivedMessageSize的问题。 - Charles Chen
我猜这是因为底层传输协议是HTTP,因此传入消息的大小和速度可能会有所不同。有时候当你读取数据时只有很少的块数据可用,而有时候会有非常大的消息流入。 - Brijesh Mishra
我在本地主机和客户端之间运行了这个程序,它非常稳定。实际上,根据进一步的研究,似乎这是WCF的一个限制,并且可以通过设置NetTcpBinding中的connectionBufferSize配置来克服,但我想知道是否也可以在基于HTTP的绑定中实现类似的功能? - Charles Chen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接