Подтвердить что ты не робот

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

Я пытаюсь создать 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 для обнаружения некоторых из этих вещей.

4b9b3361

Ответ 1

В своей книге "Программирование WCF-сервисов" Юваль Лоуи объясняет, что WCF не предоставляет mechansim для управления обратными вызовами службы, и это должно управляться службой и клиентом явно. Если служба пытается вызвать обратный вызов, который был закрыт на клиенте, на канале службы будет выбрано ObjectDisposedException.

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

Ответ 2

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

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

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