Transferring large data when using Web Services

I've been working with complex reporting application and big part of the application relies on Web Services.

The client requests some operations by calling the Web Methods. The Web Service basically does everything on the sever where it is deployed and returns data to the client as byte[] array.

When we have some more complex methods, the maximum message size quota (which is 65536 by default) is exceeded.

When trying to get the byte[] back from the Web Service

byte[] rep = serv.getReport(json);

the error that is thrown is:

The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.

As it says, the MaxReceivedMessageSize property value should be increased.

In Web.config under <system.serviceModel>, once you add web service reference, the bindings and client are generated.

It should look something like this:

<system.serviceModel>
 <bindings>
  <basicHttpBinding>
    <binding name="Service1Soap" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
      allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
      messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
    <security mode="None">
     <transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
     <message clientCredentialType="UserName" algorithmSuite="Default"/>
    </security>
   </binding>
  </basicHttpBinding>
 </bindings>
 <client>
  <endpoint address="http://hajan/mywebservapp/MyService.asmx" binding="basicHttpBinding" bindingConfiguration="Service1Soap" contract="HSServ.Service1Soap" name="Service1Soap"/>
 </client>
</system.serviceModel>

So, pay attention on the <binding name="Service1Soap ...> element:

Whole line:


<binding name="Service1Soap" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">


So, you should make change to the bolded attributes of the binding element.

Because I was not sure how big in size the byte[] array will be in the future, I have set it to the maximum of 2^31 - 1 or 2147483647 which is the biggest 32 bit number.

Next, we should pay attention on the transferMode, which by default is Buffered.

- Bufferred means that the request and response messages will be both buffered.

Other possible enumerations for transferMode are:

- Streamed which means both the request and response messages will be streamed, or

- StreamedRequest where the request will be streamed while response message buffered, or

- StreamedResponse where the request message will be buffered while response message streamed.

For those that don't know, Buffered means that the transfer will hold the entire message in memory buffer all until the transfer is completed.
Streamed means that only the message headers will be buffered, while the message body will be exposed as a stream.

In our case, after we've made change to the maxReceivedMessageSize from 65536 to 2147483647 - if we leave the transferMode="Buffered" we will get another error:

For TransferMode.Buffered, MaxReceivedMessageSize and MaxBufferSize must be the same value. Parameter name: bindingElement

True. It's required both these attributes to have same value, which is logically because the MaxReceivedMessageSize (at this moment) is larger than MaxBufferSize, and we are expecting the whole message to be buffered.

Here, we have two options:

a) You can change the MaxBufferSize to size of 2147483647

b) You can make the transferMode to: Streamed, StreamedRequest or StreamedResponse.

You can change the option you think is more suitable for your application, even streamed transfers can improve the scalability of a service especially when transferring large messages, by eliminating the need of large buffers.

In the MSDN library, I have found the following paragraph regarding decision of using buffered and streamed transfers:

"The decision to use either buffered or streamed transfers is a local decision of the endpoint for HTTP transports. For HTTP transports, the transfer mode does not propagate across a connection, or to proxy servers or other intermediaries. Setting the transfer mode is not reflected in the description of the service contract. After generating a proxy to a service, you can (it is allowed but not required) edit the configuration file for services intended to be used with streamed transfers to set the transfer mode. For TCP and named pipe transports, the transfer mode is propagated as a policy assertion."

Once you try to test it, the next error you might get is regarding the maxArrayLength property of readerQuotas element. It's all about the XML Reader of the XML data.

<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>

The error is as follows:

The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 205847.

So, we need to increase the maxArrayLength value because our byte[] array which is returned in our example exceeds the 16348 size. The max value you can set for maxArrayLength is again the max Int32 number which is equal to 2147483647. Once you are done with this setting, your web service will be able to transfer large data between your web service and the web service consumer - client application.

In the future, I will write more on the same topic using WCF, which indeed has a very bright future.
(I just got this suggestion from Vivek, who is one of the administrators in CodeASP.NET and recognized Microsoft MVP)

Hope this was useful.

Regards,
Hajan

This blog post is also posted on my CodeASP.NET community’s blog:
http://codeasp.net/blogs/hajan/microsoft-net/802/transferring-large-data-when-using-web-services2

Translation in Macedonian language:
http://mkdot.net/blogs/hajan/archive/2010/07/16/13868.aspx

Published Tuesday, July 13, 2010 9:04 PM by hajan

Comments

# Transferring large data when using Web Services - Hajan's Blog

Pingback from  Transferring large data when using Web Services - Hajan's Blog

# Twitter Trackbacks for Transferring large data when using Web Services - Hajan's Blog [asp.net] on Topsy.com

Pingback from  Twitter Trackbacks for                 Transferring large data when using Web Services - Hajan's Blog         [asp.net]        on Topsy.com

# Пренос на големи податоци при користење на Веб Сервиси

Friday, July 16, 2010 5:29 PM by Hajan's Blog

Во последниот период правев доста тестирања при работа со Веб Сервиси и пренос на податоци од серверот

# re: Transferring large data when using Web Services

Saturday, August 14, 2010 6:54 AM by Figuerres

Good Info for when you need it.

BUT:

before a developer does this i always ask them to take a look and see if they really need to do that.

many times i see folks trying to do this for the wrong reasons.

most of the time you do not need to for example send 100,000 rows of data to ma client application where the user can only see 30 rows at a time!

# re: Transferring large data when using Web Services

Saturday, August 14, 2010 9:58 AM by hajan

@Figuerres, I think you have a good point. However, this might come as an issue especially when you send binary data from the service to the client i.e. a big Document (or PDF) file converted to byte array can simply exceed the default value set in Web.config.

Thank you for your very valuable feedback.

# TWC9: Lots of Windows Phone 7 Stuff, SQL Cop, WCF for Large Data

Monday, August 16, 2010 9:25 AM by Canadian Developer Connection

Can’t see the video? You can download and install Silverlight or download the video in iPod (MP4) , MP3

# re: Transferring large data when using Web Services

Monday, August 16, 2010 1:38 PM by MichaelFouks

Thank you, good article. We are using WCF services for communication between components of our system; I did run in this problem and had to adjust endpoint configuration properties as you described. However, I did not analyze these settings and their relationships as systematically as you did. Thank you!

I saw an opinion that Microsoft introduced this parameters as a mean to limit danger of DOS attacks; I guess, the idea was to limit size and complexity of malicious data that can be sent to a web site. However, it seems to be a bit strange to me: yes, volume of a single request can be limited, but it will not help if the attacker is sending numerous requests simultaneously. The site will be overloaded anyway because too many small requests will flood it. Also, for netTCPBindings,  number of connections can exceed maximum number allowed in configuration (maxConnections) which will also lead to DOS situation.

Most annoying, these limitations serve as on-or-off switches: if one wants large messages to go through, he most probably will not be interested to limit messages to a specific size and will use the maximum numbers allowed. Yes having these settings gives additional flexibility, but does anyone need it? Would not a boolean or an enumeration saying "DOS safe mode" be easier to use?

# re: Transferring large data when using Web Services

Monday, August 23, 2010 6:37 AM by hajan

@MichaelFouks, thank you for your significant comment. As per your last two questions, I think the reason why you have option to set these values manually without having bolean YES/NO to switch on or off, is due to the flexibility. In some point, your suggestion seems good, but there might be other cases when having to switch the YES/NO mode would limit the flexibility a developer can have because as I have shown in this article, there are several settings to which I've made modifications, where in some cases each setting might serve different purpose.

# TWC9: Windows Phone samples and controls, SQL Cop, WCF for large data | The Online Blog

Pingback from  TWC9: Windows Phone samples and controls, SQL Cop, WCF for large data | The Online Blog

# re: Transferring large data when using Web Services

Monday, October 04, 2010 4:13 PM by Matman

Mine is still not working for some reason...

This is my ServiceReferences.ClientConfig file (silverlight)

<system.serviceModel>

   <bindings>

     <basicHttpBinding>

       <binding name="LargeBuffer" closeTimeout="00:01:00"

         openTimeout="00:01:00"

         receiveTimeout="00:10:00"

         sendTimeout="00:01:00"

         transferMode="Streamed"

         maxReceivedMessageSize="73400320"

         maxBufferSize="73400320"

         maxBufferPoolSize="73400320"

         useDefaultWebProxy="true"

                >

         <!--70MB-->

         <readerQuotas maxArrayLength="73400320" />

       </binding>

     </basicHttpBinding>

   </bindings>

   <client>

     <endpoint address="localhost/Service1.svc"

               binding="basicHttpBinding"

               bindingConfiguration="LargeBuffer"

               contract="WcfMsSql.IService1"

               name="LargeBuffer"/>

   </client>

   <services />

   <behaviors>

     <serviceBehaviors>

       <behavior>          

         <serviceMetadata httpGetEnabled="true"/>

         <serviceDebug includeExceptionDetailInFaults="true"/>

       </behavior>

     </serviceBehaviors>

   </behaviors>

   <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />

 </system.serviceModel>

This is my wcf service app.config file...

<system.serviceModel>

   <bindings>

     <basicHttpBinding>

       <binding name="LargeBuffer" closeTimeout="00:01:00"

         openTimeout="00:01:00"

         receiveTimeout="00:10:00"

         sendTimeout="00:01:00"

         transferMode="Streamed"

         maxReceivedMessageSize="73400320"

         maxBufferSize="73400320"

         maxBufferPoolSize="73400320"

         useDefaultWebProxy="true"

                >

         <!--70MB-->

         <readerQuotas maxArrayLength="73400320" />

       </binding>

     </basicHttpBinding>

   </bindings>

   <client>

     <endpoint address="localhost/Service1.svc"

               binding="basicHttpBinding"

               bindingConfiguration="LargeBuffer"

               contract="WcfMsSql.IService1"

               name="LargeBuffer"/>

   </client>

   <services />

   <behaviors>

     <serviceBehaviors>

       <behavior>

         <serviceMetadata httpGetEnabled="true"/>

         <serviceDebug includeExceptionDetailInFaults="true"/>

       </behavior>

     </serviceBehaviors>

   </behaviors>

   <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />

 </system.serviceModel>

Any help please..

# re: Transferring large data when using Web Services

Wednesday, October 06, 2010 8:24 AM by hajan

Hello @Matman,

Can you please post your question in the ASP.NET Forums (http://forums.asp.net) since it might procude further discussions and the forums are the most suitable place for such conversations regarding issues/questions you have.

Moreover, we will need to see the error message you get (if any) and elaborate a bit more.

Thank You,

Hajan

# re: Transferring large data when using Web Services

Tuesday, February 01, 2011 5:55 PM by wcfdev

Very helpful. Thank you!

# re: Transferring large data when using Web Services

Thursday, February 24, 2011 9:38 AM by Fang Cao

I had this exception during debug but deployed online this problem is not happening. Any idea why?

# re: Transferring large data when using Web Services

Thursday, December 08, 2011 7:16 AM by SiMmM

Thank you, well explained and very useful !

In my case I had to define the webHttpBinding with the maxReceivedMessageSize, maxBufferSize and maxArrayLength set to the maximum value (2147483647) in order to upload files from JQuery to a WCF Service.

Simon

# re: Transferring large data when using Web Services

Sunday, December 18, 2011 7:49 AM by hajan

Thanks for your comment, Simon.

Leave a Comment

(required) 
(required) 
(optional)
(required)