Question

J'essaie de créer une SOA dans laquelle les clients peuvent effectuer des requêtes longues sur le serveur et le serveur répond par un rappel.

J'aimerais pouvoir détecter si le client se déconnecte (par un arrêt, une exception non gérée ou une perte de connectivité réseau), afin que le serveur puisse choisir d'annuler la requête coûteuse.

Je teste une variété de cas d'échecs mais je ne parviens pas à faire déclencher certains gestionnaires d'événements.

Cas d'échec testés: Tuer le processus client après la demande. Utilisation d'un programme tel que CurrPorts pour fermer la connexion TCP.

Code de test:

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 type de cas d'échec? J'aimerais pouvoir utiliser la connexion TCP sous-jacente pour détecter certaines de ces choses.

Était-ce utile?

La solution

Dans son livre "Programming WCF Services", Juval Lowy explique que WCF ne fournit pas de mécanisme de gestion des rappels de service, qui doit être géré explicitement par le service et le client. Si le service tente d'appeler un rappel qui a été fermé sur le client, une exception ObjectDisposedException sera levée sur le canal de service.

Il recommande d'ajouter une méthode de connexion et de déconnexion au contrat de service. Comme le rappel doit être fourni au service lors de leur appel, le service peut gérer les rappels des clients. Il appartient ensuite au client de s’assurer qu’il appelle Disconnect lorsqu'il ne souhaite plus recevoir de rappels du service. Ce dernier doit également gérer les exceptions lorsqu’il appelle des rappels au client.

Autres conseils

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

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

myCallbackObject est dans ce cas l'objet à travers lequel vous pouvez exécuter le rappel, c'est-à-dire celui qui implémente le contrat de rappel.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top