views:

774

answers:

3

How to ensure that the WCF ChannelFactory uses Binding settings in xml configuration (MaxArrayLength is ignored)

Hi, I am new to Wcf and and writing my first Wcf service and client. I prefer not to use tools to generate config; I would rather write the config myself. The problem I am trying to solve is of a client communicating with a service over netTcp. The service can potentially return very large payloads (greather than the default readerQuotas.maxArrayLength). The components I have initially developed work fine when the byte stream payloads are relatively low (ie less than the default which it think is about 16K). I can solve this problem programmicatically by creating a binding and setting MaxArrayLength to a sufficiently large value. However, I need to be able to perform the equivalent in the xml configuration.

My app.config (client) is:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>

        <client>
            <endpoint address="net.tcp://localhost:9000/WcfDataService/RemoteDataRequesterService/"
                binding="netTcpBinding" bindingConfiguration="unsecureNetTcpBinding"
                contract="WcfDataServiceLib.IRemoteDataRequester"
                name="DataRequesterEndpoint" />
        </client>

        <bindings>
            <netTcpBinding>
                <binding name="unsecureNetTcpBinding" maxReceivedMessageSize="2147483647">
                    <readerQuotas maxArrayLength="1000000" />
                    <security mode="None" />
                </binding>
            </netTcpBinding>
        </bindings>

    </system.serviceModel>
</configuration>

The code to create the client proxy is as follows:

private void Init()
{
    var address = new EndpointAddress(@"net.tcp://localhost:9000/WcfDataService/RemoteDataRequesterService/");
    const string endpointName = "DataRequesterEndpoint";

    ChannelFactory<IRemoteDataRequester> factory = new ChannelFactory<IRemoteDataRequester>(
        endpointName);

    IRemoteDataRequester proxy = factory.CreateChannel(address);

    // call business methods on proxy ...
}

Note that the code is linked to the config by the variable 'endpointName'.

Service side config (I don't think this is relevant but included for completeness):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <services>
            <service name="WcfDataServiceLib.RemoteDataRequesterService" behaviorConfiguration="WcfDataServiceLib.RemoteDataRequesterServiceBehavior">
                <host>
                    <baseAddresses>
                        <add baseAddress = "net.tcp://localhost:9000/WcfDataService/RemoteDataRequesterService" />
                        <add baseAddress="http://localhost:8731/Design_Time_Addresses/WcfDataService/RemoteDataRequesterService/"/&gt;
                    </baseAddresses>
                </host>
                <endpoint address ="" binding="netTcpBinding" bindingConfiguration="netTcpBindingConfig" contract="WcfDataServiceLib.IRemoteDataRequester">
                    <identity>
                        <dns value="localhost"/>
                    </identity>
                </endpoint>
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

            </service>
        </services>
        <behaviors>
            <serviceBehaviors>
                <behavior name="WcfDataServiceLib.RemoteDataRequesterServiceBehavior">
                    <serviceMetadata httpGetEnabled="False"/>
                    <serviceDebug includeExceptionDetailInFaults="True" />
                </behavior>
            </serviceBehaviors>
        </behaviors>

        <bindings>
            <netTcpBinding>
                <binding name="netTcpBindingConfig" receiveTimeout="00:00:30">
                    <readerQuotas maxArrayLength="1000000"/>
                </binding>
                <binding name="netTcpReliableSession" receiveTimeout="00:00:30" >
                    <reliableSession enabled="true"/>
                </binding>
            </netTcpBinding>
        </bindings>

    </system.serviceModel>
</configuration>

When I run the client in a scenario in which a large byte stream is returned by the service, an exception is thrown and the message inside the exception is:

Communication Error occured: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:59.9687494'

(This is not a timeout as the error occurs immediately.)

As previously stated, I can fix this programmatically as follows:

var binding = new NetTcpBinding
{
    ReaderQuotas = { MaxArrayLength = 10000000 }
};

return new ChannelFactory<IRemoteDataRequester>(binding);

this works ok, but I need to do it via config for testing purposes.

I have also tried the following:

var binding = new NetTcpBinding("unsecureNetTcpBinding");
return new ChannelFactory<IRemoteDataRequester>(binding);

But this makes no difference.

So my question is why, when I create the channel from the endpoint config which includes a binding with MaxArrayLength set to a suitable value, is this value ignored?

Many regards.

Ok, I have found the solution. The configuration was working all along. However, the config that I provided ("unsecureNetTcpBinding") which I found from a code sample illustrating http services (not net tcp service which is what I'm designing). The rogue piece of config was 'security mode="None"' When I took this out, it worked. If I change readerQuotas maxArrayLength this is applied as I require. The reason why my code worked is because I was not setting the security mode to none. Thanks for your comments and assistance.

+1  A: 

I think the problem could be that in config your number only has 6 zeros, while in code you have 7 zeros. Maybe?

klausbyskov
A: 

Maybe the MaxArrayLength isn't the right property to set.

Try "maxBufferSize" and "MaxBufferPoolSize":

<bindings>
        <netTcpBinding>
            <binding name="unsecureNetTcpBinding" 
                     maxBufferSize="2147483647"
                     maxBufferPoolSize="2147483647"
                     maxReceivedMessageSize="2147483647">
                <readerQuotas maxArrayLength="1000000" />
                <security mode="None" />
            </binding>
        </netTcpBinding>
    </bindings>

But the real question is: if you have large amounts of data, why aren't you making use of WCF streaming?? That's exactly what it's designed for.

http://www.haveyougotwoods.com/archive/2008/04/14/wcf-message-streaming.aspx

The maxBufferSize etc. sizes are set to a fairly small value on purpose - to avoid denial of service attacks. Just cranking those up to MaxInt levels makes your server vulnerable to these DOS attacks.

UPDATE:
Try doing this: - create a new console app - add references to System.Runtime.Serialization and System.ServiceModel - add an app.config which contains exactly what your client side config contains - put these lines of code in your console app:

   class Program
   {
        static void Main(string[] args)
        {
            NetTcpBinding binding = new NetTcpBinding("unsecureNetTcpBinding");
            int maxArrayLength = binding.ReaderQuotas.MaxArrayLength;
            long maxReceivedMessageSize = binding.MaxReceivedMessageSize;
        }
    }
  • run and debug - what values do you get?? I get exactly what you've entered: "1000000" for binding.ReaderQuotas.MaxArrayLength, "2147483647" for binding.MaxReceivedMessageSize.

WCF does recognize and use those settings from config - 120% guaranteed. There must be something else fishy going on in your app.....

marc_s
Shantaram
I am setting maxBufferSize and MaxArrayLength in config - no problem. There must be something else happening in your case.....
marc_s
A: 

Hi thanks for your quick response. The issue of whether there are 6 or 7 zeros is irrelevant since I am either using 1 or the other not both at the same time. (I am either configuring the binding in config or programmatically).

To my knowledge so far, I think I have written the code correctly having scouted the web for examples. I should be able to modify readerQuotas/maxArrayLength in the xml and have it applied to the channelFactory. I need to be able to do this not only for maxArrayLength but for other settings also, so this is an excercise in finding out how this is done. The code that set the maxArrayLength (ReaderQuotas = { MaxArrayLength = 10000000 } ) works ok, so I would be very surpised that to get it to work in config, it wasn't .

I take your point about using WCF streaming and that is the way that I will take my design. I still need to know how to change bindings in config though.

Very much appreciate your advice,

regards.

Shantaram
you should update your original question by editing it, instead of answering it yourself.
marc_s