Detecting Client Death in Wcf Duplex Contracts

Detecting Client Death in WCF Duplex Contracts

In his 'Programming WCF Services' book, Juval Lowy explains that WCF does not provide a mechansim for managing service callbacks, and this must be managed by the service and client explicitly. If the service attempts to invoke a callback which has been closed on the client, an ObjectDisposedException will be thrown on the service channel.

He recommends adding a Connect and Disconnect method to the service contract - since the callback must be provided to the service when these are called, the service can manage client callbacks. It is then up to the client to ensure that it calls Disconnect when it no longer wishes to recieve callbacks from the service, and the service must handle any exceptions when invoking callbacks to the client.

C# - WCF Duplex = Fire an event when a client disconnect

The issue you are having is likely becuase of WsDualHttpBinding. In case you have this binding, two connections are created, from client to service and from service to client.

When the application is deployed over the internet it can create some issues with supporting such applications, you need to be sure that people are not behind the firewall or NAT or etc that can prevent your service to connect back to client.

I still don't know why it doesn't work on local machine when testing, but i will try to resolve it and update the answer.

As you told me more details in our chat, from the nature of your application it's better to use NetTcpBinding. In this case it's easier to understand what is happening cause one connection is created, and you will receive the notifications in case of gracefull close or abort of client.

As i told you before, anyway it's better to create some heartbeat mechanism to have things more reliable in case of unexpected computer or router shutdown.

Also, you can find this good cheat sheet on how to select communication between parties that involve WCF:

wcf binding cheat sheet

Handling dropped clients in a duplex binding WCF application

I did some testing where I attached handlers to the Closed and Faulted events of the callback channel, then killed the client at the point just before the callback would be invoked by the server. On each trial, the Closed/Faulted event was fired instantaneously and before the server attempted to invoke the callback. All the same, I still have the callback invocation wrapped in a try-catch block because the destruction of the client channel could occur just as another thread was entering the callback.

The only clean-up necessary was to remove the reference to the callback channel. WCF and the garbage-collector do the rest.

How to be notified if WCF Duplex session is prematurely closed

It has been a while, this is from memory so I could be wrong, but I think perhaps if you make an IEndpointBehavior that goes an pokes at the DispatchRuntime to add an IInputSessionShutdown, then you can get notified when the session channel ends.

http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.dispatchruntime.inputsessionshutdownhandlers.aspx

How can a WCF Service Raise Events to its Clients?

You cannot use events. You can use callbacks to simulate events.

WCF Server Push connectivity test. Ping()?

Your approach sounds reasonable, here are some links that may or may not help (they are not quite exactly related):

Detecting Client Death in WCF Duplex Contracts
http://tomasz.janczuk.org/2009/08/performance-of-http-polling-duplex.html

Having some health check built into your application protocol makes sense.

If you are worried about malicious clients, then add authorization.

The second link I shared above has a sample pub/sub server, you might be able to use this code. A couple things to watch out for -- consider pushing notifications via async calls or on a separate thread. And set the sendTimeout on the tcp binding.

HTH

How to handle WCF exceptions (consolidated list with code)

EDIT: There seems to be some inefficiencies with closing and reopening the client multiple times. I'm exploring solutions here and will update & expand this code if one is found. (or if David Khaykin posts an answer I'll mark it as accepted)

After tinkering around with this for a few years, the code below is my preferred strategy (after seeing this blog posting from the wayback machine) for dealing with WCF retries and handling exceptions.

I investigated every exception, what I would want to do with that exception, and noticed a common trait; every exception that needed a "retry" inherited from a common base class. I also noticed that every permFail exception that put the client into an invalid state also came from a shared base class.

The following example traps every WCF exception a client could through, and is extensible for your own custom channel errors.

Sample WCF Client Usage

Once you generate your client side proxy, this is all you need to implement it.

Service<IOrderService>.Use(orderService=>
{
orderService.PlaceOrder(request);
}

ServiceDelegate.cs

Add this file to your solution. No changes are needed to this file, unless you want to alter the number of retries or what exceptions you want to handle.

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");

public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = null;
bool success = false;

Exception mostRecentEx = null;
int millsecondsToSleep = 1000;

for(int i=0; i<5; i++) // Attempt a maximum of 5 times
{
// Proxy cann't be reused
proxy = (IClientChannel)_channelFactory.CreateChannel();

try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
break;
}
catch (FaultException customFaultEx)
{
mostRecentEx = customFaultEx;
proxy.Abort();

// Custom resolution for this app-level exception
Thread.Sleep(millsecondsToSleep * (i + 1));
}

// The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
catch (ChannelTerminatedException cte)
{
mostRecentEx = cte;
proxy.Abort();
// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}

// The following is thrown when a remote endpoint could not be found or reached. The endpoint may not be found or
// reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
catch (EndpointNotFoundException enfe)
{
mostRecentEx = enfe;
proxy.Abort();
// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}

// The following exception that is thrown when a server is too busy to accept a message.
catch (ServerTooBusyException stbe)
{
mostRecentEx = stbe;
proxy.Abort();

// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}
catch (TimeoutException timeoutEx)
{
mostRecentEx = timeoutEx;
proxy.Abort();

// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}
catch (CommunicationException comException)
{
mostRecentEx = comException;
proxy.Abort();

// delay (backoff) and retry
Thread.Sleep(millsecondsToSleep * (i + 1));
}

catch(Exception e)
{
// rethrow any other exception not defined here
// You may want to define a custom Exception class to pass information such as failure count, and failure type
proxy.Abort();
throw e;
}
}
if (success == false && mostRecentEx != null)
{
proxy.Abort();
throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
}

}
}


Related Topics



Leave a reply



Submit