6

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/"/>
                    </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.

Plastikfan
  • 3,674
  • 7
  • 39
  • 54

2 Answers2

1

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

Klaus Byskov Pedersen
  • 117,245
  • 29
  • 183
  • 222
0

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
  • 732,580
  • 175
  • 1,330
  • 1,459
  • I have tried setting maxBufferSize, maxBufferPoolSize & maxReceivedMessageSize but these don't work via the config either. Just to reiterate, I can fix this issue via code with maxArrayLength, but I want to know how to do it via config, cheers – Plastikfan Dec 03 '09 at 17:25
  • I am setting maxBufferSize and MaxArrayLength in config - no problem. There must be something else happening in your case..... – marc_s Dec 03 '09 at 17:40