简体   繁体   English

检测WCF双工合同中的客户端死亡

[英]Detecting Client Death in WCF Duplex Contracts

I'm trying to build a SOA where clients can perform long running queries on the server and the server responds using a callback. 我正在尝试构建一个SOA,客户端可以在服务器上执行长时间运行的查询,服务器使用回调进行响应。

I'd like to be able to detect if the client disconnects (through user initiated shutdown, unhandled exception or loss of network connectivity) so that the server can choose to cancel the expensive request. 我希望能够检测客户端是否断开连接(通过用户启动的关闭,未处理的异常或丢失网络连接),以便服务器可以选择取消昂贵的请求。

I'm testing a variety of failure cases but I can't seem to get certain event handlers to fire. 我正在测试各种故障情况,但我似乎无法启动某些事件处理程序。

Tested Failure Cases: Killing the Client Process After the request. 经过测试的失败案例:在请求后杀死客户端进程。 Using a program like CurrPorts to close the TCP Connection. 使用像CurrPorts这样的程序来关闭TCP连接。

Test Code: 测试代码:

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

What's the best practice to handle these sorts of failure cases? 处理这类失败案例的最佳做法是什么? I'd like to be able to use the underlying tcp connection to detect some of these things. 我希望能够使用底层的tcp连接来检测其中的一些内容。

In his 'Programming WCF Services' book, Juval Lowy explains that WCF does not provide a mechansim for managing service callbacks, and this must be managed by the service and client explicitly. 在他的“编程WCF服务”一书中,Juval Lowy解释说WCF没有提供管理服务回调的机制,这必须由服务和客户端明确管理。 If the service attempts to invoke a callback which has been closed on the client, an ObjectDisposedException will be thrown on the service channel. 如果服务尝试调用已在客户端上关闭的回调,则将在服务通道上抛出ObjectDisposedException。

He recommends adding a Connect and Disconnect method to the service contract - since the callback must be provided to the service when these are called, the service can manage client callbacks. 他建议在服务合同中添加Connect和Disconnect方法 - 因为在调用这些服务时必须向服务提供回调,该服务可以管理客户端回调。 It is then up to the client to ensure that it calls Disconnect when it no longer wishes to recieve callbacks from the service, and the service must handle any exceptions when invoking callbacks to the client. 然后由客户端确保在不再希望从服务接收回调时调用Disconnect,并且服务必须在调用客户端的回调时处理任何异常。

try this to check if the callback object is still valid: 尝试这个来检查回调对象是否仍然有效:

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

myCallbackObject in this case is the object through which you can perform the callback, ie the one implementing the callback contract 在这种情况下,myCallbackObject是您可以通过其执行回调的对象,即实现回调协定的对象

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM