Détection du décès d'un Client dans les contrats Duplex de la FMC

j'essaye de construire un SOA où les clients peuvent effectuer des requêtes de longue durée sur le serveur et le serveur répond en utilisant un callback.

j'aimerais être en mesure de détecter si le client se déconnecte (par l'arrêt initié par l'utilisateur, l'exception débranchée ou la perte de connectivité réseau) de sorte que le serveur peut choisir d'annuler la demande coûteuse.

je teste une variété de cas d'échec mais je ne peux pas sembler obtenir certains feu.

Cas De Défaillance Éprouvés: Tuer le processus Client après la demande. Utiliser un programme comme CurrPorts pour fermer la connexion TCP.

Code D'Essai:

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);
        }
    }
}

Quelle est la meilleure pratique pour gérer ce genre de cas? J'aimerais pouvoir utiliser la connexion tcp sous-jacente pour détecter certaines de ces choses.

31
demandé sur Sindhudweep 2009-09-15 19:35:41

2 réponses

dans son livre "Programming WCF Services", Juval Lowy explique que la WCF ne fournit pas de mécanisme de gestion des callbacks de service, et cela doit être géré explicitement par le service et le client. Si le service Tente d'invoquer un rappel qui a été fermé sur le client, une ObjectDisposedException sera lancée sur le canal de service.

il recommande d'ajouter une méthode de connexion et de déconnexion au contrat de service-puisque le rappel doit être fourni le service lorsqu'ils sont appelés, le service peut gérer les rappels de client. Il appartient alors au client de S'assurer que ses appels se déconnectent lorsqu'il ne souhaite plus recevoir de rappels du service, et le service doit gérer toutes les exceptions lorsqu'il invoque les rappels au client.

17
répondu Lee 2009-09-15 16:32:47

essayez ceci pour vérifier si l'objet callback est toujours valide:

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

myCallbackObject dans ce cas est l'objet à travers lequel vous pouvez effectuer le rappel, c'est à dire la mise en œuvre du contrat de rappel

12
répondu Cedric Mamo 2012-10-26 08:53:29