C# 在 WCF 双工合约中检测客户端死亡

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1427926/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 16:25:48  来源:igfitidea点击:

Detecting Client Death in WCF Duplex Contracts

c#wcfcallbackduplexnettcpbinding

提问by Sindhudweep

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 连接来检测其中的一些东西。

采纳答案by Lee

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. If the service attempts to invoke a callback which has been closed on the client, an ObjectDisposedException will be thrown on the service channel.

在他的“Programming WCF Services”一书中,Juval Lowy 解释说 WCF 不提供管理服务回调的机制,这必须由服务和客户端明确管理。如果服务尝试调用已在客户端关闭的回调,则会在服务通道上抛出 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. 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.

他建议在服务契约中添加一个 Connect 和 Disconnect 方法——因为在调用这些时必须向服务提供回调,服务可以管理客户端回调。然后由客户端确保它不再希望从服务接收回调时调用 Disconnect,并且服务必须在调用客户端回调时处理任何异常。

回答by Cedric Mamo

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, i.e. the one implementing the callback contract

在这种情况下,myCallbackObject 是您可以执行回调的对象,即实现回调合约的对象