Best Practice with WCF ChannelFactory and connection timeout
Asked Answered
K

3

7

I am working on an winform application that will access a WCF service self-hosted as a windows service. I am using the ChannelFactory instead of the service reference. I have been successful in connecting and calling the WCF service. The issue is when I let the application remain idle for 20 minutes and then try to make another call. I receive the following error:

"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.9489970'."

I am looking for the best practice on managing the connection. I currently have created a function called PrepareWCFConnection (see below) that checks state of the channel and the ChannelFactory. I call this method before I make any calls to the WCF services. Is there a better way of handling this?

     public bool PrepareWCFConnection()
    {
        if ((channelFactory == null) || 
            (channelFactory.State == CommunicationState.Faulted) ||
            (channelFactory.State != CommunicationState.Opened))
        {
            channelFactory = new ChannelFactory<IService1>(new NetTcpBinding(), endpointAddress);
        }


        if ((proxy == null) ||
            (((IClientChannel)proxy).State == CommunicationState.Faulted) ||
            (((IClientChannel)proxy).State != CommunicationState.Opened))
        {
            proxy = channelFactory.CreateChannel(endpointAddress);
            ((IClientChannel)proxy).Open();
        }

        return true;
    }
Kristine answered 16/12, 2011 at 18:41 Comment(6)
More testing of the above code proved it to not work. Both the ChannelFactory and channel are open but I still get this error after letting the system go idle: 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.9479970'.Kristine
Here is a link from MSDN that shows creating the channelfactory and channel, make the calls and close the channel and then close the channel factory. However, if you use creditials to authenticate, wouldnt closing the channel after each method call and re-creating the channel before each method be expensive on resources and time? msdn.microsoft.com/en-us/library/ms734681.aspxKristine
After testing more, I first start the call to the WCF service with PrepareWCFConnection() ... then call my service method and then call ((IClientChannel)proxy).Close(); This is closing the channel connection and then creating a new channel for each method call. Is this the best practice?Kristine
I would always close the channel after each call. If you keep it open, but inactive you will receive an CommunicationFaultedException after 10 mins. If you really want to keep you channel open, take a look at ReliableService. Don't close the ChannelFactory, but reuse it because it does all the initial configuration.Deferent
Take a look at this: codeproject.com/Tips/507328/Creating-and-closing-of-WCF-proxiesDeferent
Just saw that this is about a year old.Deferent
S
6

If you want to reuse an existing channel you need to Keep the channel alive by pinging the service every 9 minutes.I think the default receive timeout is 10 minutes, so the channel will be disconnected if it is kept idle beyond this.Or you can use reliable sessions to keep channels alive.

If you don't need to callback on the same channel , it is better you close the channel once you are done and recreate a new channel for every service operation.It is not expensive to create channels.You can cache the channel factory , but create channels for every call.

Sandman answered 12/2, 2013 at 10:44 Comment(1)
good point: You can cache the channel factory , but create channels for every call.Diwan
B
2

I know this question is quite old now but I see it didn't really get answered. There are two timeouts (well only 1 if you don't use reliable messaging) you should be concerned with when it comes to channels timing out. On the service side you have the "ReceiveTimeout" which is fired if no application messages are received within the timeout period. The default for this timeout is 10 minutes.

There is also the "InactivityTimeout" which is only used if "ReliableSession" is enabled. This timeout is the maximum duration that the channel allows the other communicating party not to send any messages before faulting the channel.

To increase the lifetime of your channel I recommend you enable "ReliableSession" and then set both your "ReceiveTimeout" & "InactivityTimeout" to a higher value. ReliableSession keeps the channel alive by sending ILM's (infrastructure level messages) like keep-alive's (ack's are also sent). If a keep-alive or an ALM (application level message) is not received before the "InactivityTimeout" expires the channel will fault.

Furthermore if an ALM (application level message) has not been received before the "ReceiveTimeout" expires the channel will fault.

So, it is recommended to either increase both timeouts to the same value OR set the "ReceiveTimeout" to a higher value than the "InactivityTimeout".

A side note, setting the "ReceiveTimeout" will have no effect when set on the client side, it is a service side timeout only. But when using ReliableSession on the service side, the client also has to implement it like so:

NetTcpBinding binding = new NetTcpBinding
        {
            ReliableSession = { Enabled = true },             
            SendTimeout = TimeSpan.FromMinutes( 1 )
        };

        binding.ReliableSession.InactivityTimeout = TimeSpan.Parse( "24.20:31:23.6470000" );

And the app.config on the service side would look something like this:

<bindings>
    <netTcpBinding>
      <binding name="netTestTcpBinding"
               receiveTimeout="24.20:31:23.6470000">
        <reliableSession inactivityTimeout="24.20:31:23.6470000"
                         enabled="true" />
      </binding>
    </netTcpBinding>
  </bindings>
<services>
  <service>
    <endpoint address="IServiceContract"
              binding="netTcpBinding"
              bindingConfiguration="netTestTcpBinding"
              name="serviceContractTcpBinding"/>
    <host>
        <baseAddresses>
             <add baseAddress="net.tcp://localhost:12001/" />
        </baseAddresses>
     </host>
    </service>                   
</services>
Brandon answered 8/2, 2016 at 15:6 Comment(0)
L
0

One pretty straight forward solution to reuse your channel, without polling or do fancy stuff it's just take care of the last call to your channel and check the wcfC.Binding.ReceiveTimeout in order to regenerate it if needed, something like:

    TimeSpan timeSpan = DateTime.Now - LastCallTime;
    if (timeSpan.TotalSeconds > wcfC.Binding.ReceiveTimeout.TotalSeconds || wcfC.State != CommunicationState.Opened)
    { 
       wcfC.Abort();
       wcfC = new WCFChannel();

    }    
    LastCallTime = DateTime.Now;
Liriodendron answered 8/6, 2017 at 11:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.