12

I'm writing a service that will only get calls from the local host. Performance is important so I thought I'd try the NetNamedPipeBinding instead of NetTcpBinding and see If I could see any noticeable performance gains.

If a client, after having performed one or more requests to the server, is idle for a longer period of time the next request will fail seemingly due to some idle timeout in the binding. The same thing also happens when the service gets restarted.

I need my clients to be able to keep a connection open for as long as it's allowed in order to avoid the overhead associated with setting up a new connection. I also need to be able to restart the service from time to time and have the clients to automatically retry if they notice that the connection has been terminated.

I know that this is suppported by the reliability stuff in NetTcpBinding but how would one go about getting the same level of re-connect reliability in the NetNamedPipeBinding? Is it even possible?

The question is somewhat academic as it isn't a requirement to use NetNamedPipes, I could just as easily adopt it to use the tcp-binding but It's an itch and I'd really like to scratch it.

Markus Olsson
  • 22,402
  • 9
  • 55
  • 62
  • IIRC, the second-last parameter of CreateNamedPipe (the unmanaged win32 function underneath NamedPipeBinding - http://msdn.microsoft.com/en-us/library/windows/desktop/aa365150%28v=vs.85%29.aspx) acts as a client connect timeout, which is rather short. This may have something to do with the timeout you see when the server starts; maybe you could user reflector/dotpeek/the debugger to see which parameters are passed down from WCF to the native function, and if those parameters can be changed using configuration – Lorenzo Dematté Jan 24 '13 at 13:40
  • Since the question is samewhat academic, in general I would proceed in this way: look at which native functions are called, with which timeouts, and then trace back to where these functions are called from managed code, to see where the parameters originated. Long, but fun and helps you figuring out how it really works :) I debugged a Sharepoint issue this way... – Lorenzo Dematté Jan 24 '13 at 13:44

3 Answers3

23

My experience is that, when using NetNamedPipes, the "ReceiveTimout" on the binding functions like an "Inactivity Timeout" rather than a receive timout. Note that this different than how a NetTCPBinding works. With TCP, it really is a receive timeout and there's a separate inactivity timeout you can configure via reliable messaging. (It's also appears to be contrary to the SDK documentation, but oh well).

To fix this, set the RecieveTimout to something big when you create the binding.
For example, if you are creating your binding procedurally...

NetNamedPipeBinding myBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
myBinding.ReceiveTimeout = TimeSpan.MaxValue;

Or, if you care creating your binding declaratively...

<netNamedPipeBinding>
    <binding name="myBinding" receiveTimeout="infinite">
    </binding>
</netNamedPipeBinding>
LPL
  • 16,827
  • 6
  • 51
  • 95
Matthew Hess
  • 261
  • 2
  • 2
  • Thanks for that tip. I had set my (receive) timeouts to 20:00 (20 min), and none of my operations took more than 2 minutes...maybe even 5 seconds more likely, and the thing was acting up. People should also look at this microsoft "feature" with the using statement that was also getting me. https://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue – granadaCoder Mar 07 '18 at 12:36
18

I haven't used NetNamedPipes in WCF but I spent more time than I cared to learning the timeout values for NetTcp. I use the following configs for my NetTcpBindings and had good luck with the connection staying active.

Server:

<binding name="MyBindingName" sendTimeout="00:00:30" receiveTimeout="infinite">
    <reliableSession enabled="true" inactivityTimeout="00:05:00" ordered="true" />
    <security mode="None" />
</binding>

Client:

<binding name="MyBindingName" closeTimeout="00:00:30" openTimeout="00:00:30" receiveTimeout="infinite" sendTimeout="00:00:30">
    <reliableSession enabled="true" inactivityTimeout="00:01:00" ordered="true" />
    <security mode="None" />
</binding>

The important settings that I spent the most time on are the sendTimeout and receiveTimeout. If your receiveTimeout is the same or less than your send, the channel will drop once that timeout is reached. If the receive is higher and the send is above a threshold, the channel will fire a transport level keepalive. From my tests, the sendTimeout threshold is 30 seconds. Anything less than that and the keepalives aren't sent.

Additionally, I have a timer based keepalive call that I execute every minute to try and ensure the channel is up and working well. The call is simply to a boolean return member:

[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)]
bool KeepAlive();

public bool KeepAlive()
{
    return true;
}

You can also grab the channel events (if you get them at the right time) and reopen the connection if something bad happens:

InstanceContext site = new InstanceContext(this);
_proxy = new MyServiceChannel(site);
if (_proxy != null) 
{
    if (_proxy.Login()) 
    {
        //Login was successful
        //Add channel event handlers so we can determine if something goes wrong
        foreach (IChannel a in site.OutgoingChannels) 
        {
            a.Opened += Channel_Opened;
            a.Faulted += Channel_Faulted;
            a.Closing += Channel_Closing;
            a.Closed += Channel_Closed;
        }
    }
}

I hope some of this translates and has value for you with NetNamedPipes.

Edit: More options for capturing the server restarted issue

When the server restarts it should cause the client's channel to either close or fault. Capturing those events on the client side would give you the option of using reconnect timer until the service is available again.

private void Channel_Faulted(object sender, EventArgs e)
{
    IChannel channel = sender as IChannel;
    if (channel != null) 
    {
        channel.Abort();
        channel.Close();
    }

    //Disable the keep alive timer now that the channel is faulted
    _keepAliveTimer.Stop();

    //The proxy channel should no longer be used
    AbortProxy();

    //Enable the try again timer and attempt to reconnect
    _reconnectTimer.Start();
}

private void _reconnectTimer_Tick(object sender, System.EventArgs e)
{
    if (_proxy == null) 
    {
        InstanceContext site = new InstanceContext(this);
        _proxy = new StateManagerClient(site);
    }
    if (_proxy != null) 
    {
        if (_proxy.Login()) 
        {
            //The connection is back up
            _reconnectTimer.Stop();
            _keepAliveTimer.Start();
        }
        else 
        {
            //The channel has likely faulted and the proxy should be destroyed
            AbortProxy();
        }
    }
}

public void AbortProxy()
{
    if (_proxy != null) 
    {
        _proxy.Abort();
        _proxy.Close();
        _proxy = null;
    }
}

You would want to ensure the reconnect timer's login attempts are done on a background thread asynchronously so they don't hang the UI every time they attempt to login. YMMV

Chris Porter
  • 3,627
  • 25
  • 28
  • Thanks Chris but modifying timeouts and adding a keepalive method would unfortunately only solve the first of my two problems. I'd still have the same problem when the service gets restarted. – Markus Olsson Dec 04 '08 at 17:33
  • 2
    how do you set binding/reliableSession programmatically? – Ivan G. Aug 15 '11 at 16:29
14

I have been looking into the problem of dropped TCP connections for two days now and came to the conclusion that a lot of people are missing a crutial point when setting up connections in WCF. What everybody seems to be doing is create a channel once and then trying to hold on to it for the lifetime of the application, playing all sorts of dirty tricks to keep the TCP session alive. This is not how it was meant to be.

You should create a channel, perform one (or some more shortly after the first) calls on your service, then close and dispose the channel. This will give you (virtually) stateless operation and you don't have to be bothered with keeping sessions alive, which you should not want in the first place.

You will find that the overhead of creating and closing a channel (from a reused ChannelFactory) is negligible, taking only some tens of nanoseconds on a typical machine.

The receiveTimeout attribute that everyone is cranking up defines the time a channel can remain idle before it is automatically dropped, which tells you channels are not meant to be kept open for very long (the default is 1 minute). If you set receiveTimeout to TimeSpan.MaxValue it will keep your channel open longer but this is not what it is for nor what you want in a practical scenario.

What finally got me on the right track was http://msdn.microsoft.com/en-us/library/ms734681.aspx which provides a horribly buggy example yet does show how one should go about using ChannelFactory. Responders point out the bugs and set the record straight so all in all you can get everything you need here.

And then, all my problems were over. No more "An operation was attempted on something that is not a socket" and no more "An existing connection was forcibly closed by the remote host".

Martin Maat
  • 141
  • 1
  • 2
  • http://social.msdn.microsoft.com/forums/en-US/wcf/thread/d5ab7e93-ff03-4d4f-badc-91423236e49b - Dave Cliffe (MSFT) suggests that this is a fairly expensive operation and that channels should be reused if possible – Roman Starkov Mar 01 '10 at 18:09
  • @Martin: "the overhead of creating and closing a channel ... is negligible". Does that not depend on things like security negotiation? I imagine renegotiating credentials with something like an AD server is far from negligible. – Xiaofu Mar 13 '10 at 09:44
  • @romkyns: There's a good MSDN blog on the performance improvements to ClientBase that came with .NET 3.5 here: http://blogs.msdn.com/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx – Xiaofu Mar 13 '10 at 09:46
  • 1
    how about "duplex" communication? Server To Client on events etc? – svrist Nov 24 '11 at 13:13
  • 1
    Yes, even I want to know what about duplex channels. How can server send data back to subscribed clients if client closes the channel after subscribing? I am trying to hold on to that channel using which client subscribes to server events. – vivek.m Jun 22 '12 at 10:25
  • I know this is an old thread, but yes I agree with regards to duplex channels - the callback channel obviously needs to remain open. – Dan Jan 05 '16 at 15:44
  • I would also be interested in duplex set ups too? – darbid Feb 11 '20 at 09:26