2012-06-29 2 views
4

Я пытаюсь создать пример потоковой передачи WCF только для целей тестирования, и я не могу быть уверенным, что это действительно потоковая передача.Режим потоковой передачи 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); 
         } 
        } 
       } 
      } 
     } 
    } 
} 

Я прочитал ряд других сообщений по этому вопросу, но не могу найти какие-либо улики.

+0

Я хотел бы добавить, что я могу заставить его работать нормально, если я установить 'MaxReceivedMessageSize' для' Int32.MaxValue'. Хотя казалось бы, что это больше не «потоковая передача» в диспетчере задач Windows, это подтверждает, что профиль памяти отличается, когда я устанавливаю его в «Streamed» и «Buffered». Тем не менее, мне все же интересно, почему - в потоковом режиме - мне нужно установить «MaxReceivedMessageSize» на «Int32.MaxValue» и почему мои чтения настолько малы. –

ответ

3

Хотя ваш пост был довольно давно, я наткнулся на него и подумал, что поделюсь своими выводами.

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

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

MaxReceivedMessageSize: Максимальный размер, в байтах принятого сообщения, в том числе заголовков, до того, как транспортное вызывает исключение.

MaxBufferSize: Максимальный размер в байтах буфера, используемого для потоковой передачи данных. Если эта транспортная квота не установлена ​​или транспорт не использует потоковое вещание, то значение квоты будет таким же, как меньшее из значения квоты MaxReceivedMessageSize и MaxValue.

Почему у вас всегда есть куски 1536 bytes, я не совсем уверен.Но я думаю, что это из-за максимального размера локальных сетей кадра (за исключением больших кадров):

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

+0

Интересные выводы (и своевременные), потому что я только что написал еще одну реализацию потоковой службы и столкнулся с той же проблемой, когда вам нужно установить «MaxReceievedMessageSize». 1536 все еще озадачивает меня, потому что кажется, что сам WCF будет абстрагировать нижние уровни сетевого стека, так что размер кадра Ethernet не имеет значения (другими словами, дождаться достаточного количества кадров, которые должны быть получены до возвращения буфера). Хорошие заметки, в любом случае! –

1

MaxReceivedMessageSize предотвращает атаку DOS при включении каналов и MaxBufferSize управляет сообщением буферизации на канале. Когда канал настроен для потоковой передачи только заголовка мыла, буферизуется и тело течет, размер блока потока контролируется реализацией службы (8 Кб в вашем case), а maxrecievedmessagesize - размер файла + header. Максимальный размер сообщения должен быть равен размеру файла MaxBufferSize в режиме буферизации. Но при потоковой передаче MaxBufferSize должен быть небольшим и MaxRecievedMessafeSize ожидаемым размером файла. В потоковом режиме MaxBufferSize может использоваться для предотвращения атаки DOS.

+0

Это хорошая информация, но я не думаю, что она отвечает на мой вопрос. В основном, в потоковом режиме, когда я установил 'MaxReceivedMessageSize' того же размера, что и' MaxBufferSize', и в моем потоке прочитал, я установил свой буфер на 8192 байта, почему я только читаю 1536 за раз и почему я буду запускать в проблему с превышением MaxReceivedMessageSize'. –

+0

Я бы угадал его, потому что базовый транспорт является http, из-за этого размера входящего сообщения и скорости, с которой они приходят, может отличаться. в то время, когда у вас есть только доступные данные, которые вы читаете, когда вы читаете в какое-то время, может произойти очень большое сообщение. –

+0

Я провел локально между хостом и клиентом, которые сидят на моей машине. Это довольно непротиворечиво. На самом деле, на основе дополнительного reserach, кажется, что это ограничение WCF и может быть преодолено в случае «NetTcpBinding», установив конфигурацию 'connectionBufferSize', но мне интересно, возможно ли подобное подобное с привязками на основе HTTP? –

Смежные вопросы