Обнаружение смерти клиента в дуплексных контрактах WCF

StackOverflow https://stackoverflow.com/questions/1427926

Вопрос

Я пытаюсь создать SOA, где клиенты могут выполнять длительные запросы на сервере, а сервер отвечает обратным вызовом.

Я хотел бы иметь возможность обнаруживать, отключается ли клиент (через инициированное пользователем завершение работы, необработанное исключение или потеря сетевого подключения), чтобы сервер мог отменить дорогостоящий запрос.

Я тестирую различные случаи сбоев, но, похоже, не могу запустить определенные обработчики событий.

Проверенные случаи отказа: Уничтожение клиентского процесса после запроса. Использование программы типа CurrPorts для закрытия TCP-соединения.

Тестовый код:

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

Как лучше всего обрабатывать подобные случаи сбоев? Я хотел бы иметь возможность использовать основное соединение TCP для обнаружения некоторых из этих вещей.

Это было полезно?

Решение

В своей книге «Программирование служб WCF» Джувал Лоуи объясняет, что WCF не предоставляет механизм управления обратными вызовами службы, и этим должен управлять сервис и клиент в явном виде. Если служба пытается вызвать обратный вызов, который был закрыт на клиенте, исключение ObjectDisposedException будет сгенерировано на канале службы.

Он рекомендует добавить метод Connect и Disconnect к контракту на обслуживание - поскольку обратный вызов должен быть предоставлен службе при ее вызове, служба может управлять обратными вызовами клиента. Затем клиент должен убедиться, что он вызывает Disconnect, когда он больше не хочет получать обратные вызовы от службы, и служба должна обрабатывать любые исключения при вызове обратных вызовов для клиента.

Другие советы

попробуйте это, чтобы проверить, является ли объект обратного вызова все еще действительным:

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

myCallbackObject в этом случае - это объект, с помощью которого вы можете выполнить обратный вызов, то есть объект, реализующий контракт обратного вызова

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top