Detectar la muerte del cliente en los contratos dúplex de WCF

Intento crear una SOA donde los clientes puedan realizar consultas de larga ejecución en el servidor y el servidor responde con una callback.

Me gustaría poder detectar si el cliente se desconecta (mediante el apagado iniciado por el usuario, la excepción no controlada o la pérdida de conectividad de red) para que el servidor pueda optar por cancelar la costosa solicitud.

Estoy probando una variedad de casos de falla, pero parece que no puedo activar ciertos controladores de eventos.

Casos de falla probados: aniquilación del proceso del cliente después de la solicitud. Usando un progtwig como CurrPorts para cerrar la conexión TCP.

Código de prueba:

using System; using System.ServiceModel; using System.Threading; namespace WCFICommunicationObjectExperiments { class Program { static void Main(string[] args) { var binding = new NetTcpBinding(SecurityMode.None); var serviceHost = new ServiceHost(typeof (Server)); serviceHost.AddServiceEndpoint(typeof (IServer), binding, "net.tcp://localhost:5000/Server"); serviceHost.Open(); Console.WriteLine("Host is running, press  to exit."); Console.ReadLine(); } } [ServiceContract(CallbackContract = typeof(IClient))] public interface IServer { [OperationContract] void StartProcessing(string Query); } public interface IClient { [OperationContract] void RecieveResults(string Results); } [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] public class Server : IServer { public void StartProcessing(string Query) { Thread.Sleep(5000); //Callback Channel var clientCallback = OperationContext.Current.GetCallbackChannel(); var clientCallbackCommunicationObject = ((ICommunicationObject) clientCallback); EventHandler faultedHandlerCallback = (o, s) => Console.WriteLine("Client Channel Faulted."); EventHandler closedHandlerCallback = (o, s) => Console.WriteLine("Client Channel Closed."); clientCallbackCommunicationObject.Faulted += faultedHandlerCallback; clientCallbackCommunicationObject.Closed += closedHandlerCallback; //Request Channel var requestChannel = OperationContext.Current.Channel; EventHandler faultedHandlerRequest = (o, s) => Console.WriteLine("Request Channel Faulted."); EventHandler closedHandlerRequest = (o, s) => Console.WriteLine("Request Channel Closed."); requestChannel.Faulted += faultedHandlerRequest; requestChannel.Closed += closedHandlerRequest; try { clientCallback.RecieveResults("42."); } catch (CommunicationObjectAbortedException ex) { Console.WriteLine("Client Aborted the connection"); } catch (CommunicationObjectFaultedException ex) { Console.WriteLine("Client Died."); } clientCallbackCommunicationObject.Faulted -= faultedHandlerCallback; clientCallbackCommunicationObject.Faulted -= closedHandlerCallback; requestChannel.Faulted -= faultedHandlerRequest; requestChannel.Closed -= closedHandlerRequest; } } public class ClientToTestStates : IClient { private IServer m_Server; private readonly ManualResetEvent m_ReceivedEvent = new ManualResetEvent(false); private readonly ManualResetEvent m_ChannelFaulted = new ManualResetEvent(false); private readonly ManualResetEvent m_ChannelClosed = new ManualResetEvent(false); public ClientToTestStates() { var binding = new NetTcpBinding(SecurityMode.None); var channelFactory = new DuplexChannelFactory(this, binding, new EndpointAddress("net.tcp://localhost:5000/Server")); m_Server = channelFactory.CreateChannel(); ((ICommunicationObject)m_Server).Open(); ((ICommunicationObject)m_Server).Faulted += ChannelFaulted; ((ICommunicationObject)m_Server).Closed += ChannelClosed; m_Server.StartProcessing("What is the answer?"); WaitHandle.WaitAny(new WaitHandle[] {m_ReceivedEvent, m_ChannelFaulted, m_ChannelClosed}); } void ChannelFaulted(object sender, EventArgs e) { m_ChannelFaulted.Set(); Console.WriteLine("Channel Faulted."); } void ChannelClosed(object sender, EventArgs e) { m_ChannelClosed.Set(); Console.WriteLine("Channel Closed."); } public void RecieveResults(string results) { m_ReceivedEvent.Set(); Console.WriteLine("Recieved Results {0}", results); } } } 

¿Cuál es la mejor práctica para manejar este tipo de casos de falla? Me gustaría poder usar la conexión tcp subyacente para detectar algunas de estas cosas.

En su libro ‘Programming WCF Services’, Juval Lowy explica que WCF no proporciona un mecanismo para gestionar las devoluciones de llamadas de los servicios, y esto debe ser gestionado por el servicio y el cliente de forma explícita. Si el servicio intenta invocar una callback que se ha cerrado en el cliente, se lanzará una ObjectDisposedException en el canal de servicio.

Recomienda agregar un método de conexión y desconexión al contrato de servicio: dado que la callback se debe proporcionar al servicio cuando se llaman, el servicio puede gestionar las devoluciones de llamada de los clientes. Depende entonces del cliente asegurarse de que llama a Desconectar cuando ya no desea recibir devoluciones de llamada del servicio, y el servicio debe manejar cualquier excepción al invocar devoluciones de llamada al cliente.

intente esto para verificar si el objeto de callback todavía es válido:

 (((ICommunicationObject)myCallbackObject).State == CommunicationState.Opened) 

myCallbackObject en este caso es el objeto a través del cual puede realizar la callback, es decir, el que implementa el contrato de callback