2009-09-15 10 views
28

Estoy tratando de construir una SOA, donde los clientes pueden realizar consultas de larga ejecución en el servidor y el servidor responde usando una devolución de llamada.Detección de Muerte cliente de WCF Duplex Contratos

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 fracaso pero parece que no puede obtener ciertos controladores de eventos para el fuego.

Probado casos de fracaso: Matar el proceso de cliente después de la solicitud. Usando un programa 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 <ENTER> 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<IClient>(); 
      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<IServer>(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 fracaso? Me gustaría poder usar la conexión tcp subyacente para detectar algunas de estas cosas.

+0

usted tiene intenté encender la confiabilidad? TCP proporciona confiabilidad punto a punto.
La fiabilidad de los mensajes (a través de WS-Fiabilidad) proporciona fiabilidad de extremo a extremo.
Y a su vez, creo que se le avisará de cualquier lado ‘desaparece’ sin contemplaciones. Para los transportes que lo admitan, es una buena práctica activar siempre la confiabilidad, aunque es posible que algunos "errores" de la red no lo admitan. –

Respuesta

14

En su libro 'Programación de WCF Services', Juval Lowy explica que WCF no proporciona un mecanismo para administrar las devoluciones de llamadas de servicio, y esto debe ser gestionado por el servicio y el cliente de forma explícita. Si el servicio intenta invocar una devolución de llamada que se ha cerrado en el cliente, se lanzará una ObjectDisposedException en el canal de servicio.

Se recomienda agregar un método Connect y Disconnect para el contrato de servicios - ya que la devolución de llamada debe ser proporcionada al servicio cuando éstos se llaman, el servicio puede gestionar las devoluciones de llamada de cliente. 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.

+1

Gracias por la información. En el caso de una falla inesperada del cliente donde no se puede esperar que se llame a Disconnect(), ¿qué se puede hacer para detectar esa falla desde el lado del servidor para liberar recursos valiosos? – Sindhudweep

+1

dado que el sistema operativo sabe que la conexión TCP no tiene los paquetes de mantener activo. Debería ser posible para un servidor WCF saber que el cliente se ha ido. Entonces debería haber una mejor respuesta que esto. ¡Simplemente no sé lo que es! –

+0

El sistema operativo puede usar la conexión TCP. En mis pruebas estaba usando el enlace Net.tcp y recibía eventos con errores en el canal. Desafortunadamente, si la conexión TCP es una máquina que está sirviendo como un relé (usando desvío remoto de SSH con la unión mundial) el sistema operativo no ve la conexión TCP estrecha (ya que la conexión a la máquina de relé no tiene en realidad cerrado). Esto estaba causando el extraño comportamiento que observé en mi prueba. – Sindhudweep

12

probar esto para comprobar si el objeto de devolución de llamada sigue siendo válido:

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

myCallbackObject en este caso es el objeto a través del cual se puede realizar la devolución de llamada, es decir, la aplicación del contrato de devolución de llamada

+5

no se recomienda esta solución porque el canal de devolución de llamada puede tener una falla entre el momento en que comprueba el estado y la hora en que hace algo con el canal. – LxL

+2

Sí, aún debe manejar las excepciones ya que no hay realmente una manera de verificarlo con certeza. –

Cuestiones relacionadas