Prerequisites for Implementing a Keep-Alive Mechanism in WCF 3.0
There seems to be a common misconception that, in order to find out whether a WCF session is still alive, one has to implement some kind of custom ping or heartbeat operation on the service, which is to be regularly called by the client application. However, the WCF framework, when configured correctly, already does this for you in the background.
The key to this feature is the
ReliableSession class. When
ReliableSession.Enabled is true, WCF will continuously generate internal infrastructure messages to check whether the service is still ready. After the service has been dead for some specified time, the proxy's
ClientBase<TChannel>.State is automatically set to
Faulted, and the
ICommunicationObject.Faulted event is raised, giving the client application a chance to react. The delay between the service breakdown and the Faulted event can be specified through the
ReliableSession.InactivityTimeout. If, for instance, the
InactivityTimeout is set to four minutes, WCF will generate several infrastructure messages during every four minutes. Therefore, if you shut down the service, the Faulted event will be raised on the client after four to six minutes. If you set the
InactivityTimeout to 20 seconds, it will be raised after 20 to 30 seconds. The exact duration depends on WCF-internal implementation details.
In order for this to work, you also have to set the
Binding.ReceiveTimeout to at least the duration of the
ReceiveTimeout specifies how long the service will stay alive without any service operations being called from the client application. If, for instance, you expect your client application to call service operations at least twice an hour, you may set the
ReceiveTimeout to 30 minutes, but still let the
InactivityTimeout be 4 minutes or so, in order that the client application can take measures in the background when the service is temporarily interrupted.
The following example shows how to define both timeouts on the client and server sides, either configuratively (in the App.config file), or imperatively (in code). The example presupposes that you are using a reliable session-capable binding (such as
NetTcpBinding) and that you have applied the
OperationContractAttribute.IsTerminating properties in meaningful ways. If you are not sure about that, you may want to read the respective MSDN documentation entries first. A comparison table of the system-provided bindings can be found here. Furthermore, if you do not like hand-coding configuration files, you can use the WCF service Configuration Editor Tool, which is installed with the Microsoft Windows SDK.
Defining the Timeouts Configuratively
<configuration> <system.serviceModel> <!-- In the App.config files of the service host and client apps: --> <bindings> <netTcpBinding> <binding name="myNetTcpBinding" receiveTimeout="00:30:00"> <reliableSession enabled="true" inactivityTimeout="00:04:00"/> </binding> </netTcpBinding> </bindings> <!-- In the App.Config file of the service host app: --> <services> <service name="MyTest.MyService"> <endpoint address="net.tcp://localhost/MyService/" binding="netTcpBinding" bindingConfiguration="myNetTcpBinding" contract="MyTest.IMyService"/> </service> </services> <!-- In the App.Config file of the client app: --> <client> <endpoint address="net.tcp://localhost/MyService/" binding="netTcpBinding" bindingConfiguration="myNetTcpBinding" contract="MyTest.IMyService"/> </client> </system.serviceModel> </configuration>
Defining the Timeouts Imperatively
// using System; // using System.ServiceModel; // In the service host and client apps: var myNetTcpBinding = new NetTcpBinding(); myNetTcpBinding.ReceiveTimeout = new TimeSpan(0, 30, 0); myNetTcpBinding.ReliableSession.Enabled = true; myNetTcpBinding.ReliableSession.InactivityTimeout = new TimeSpan(0, 4, 0); // In the service host app: var host = new ServiceHost(typeof(MyService)); host.AddServiceEndpoint(typeof(IMyService), myNetTcpBinding, "net.tcp://localhost/MyService"); // In the client app: var factory = new ChannelFactory<IMyService>(myNetTcpBinding, "net.tcp://localhost/MyService"); var channel = factory.CreateChannel();