简体   繁体   English

WCF服务如何检测客户端断开连接

[英]How can WCF service detect client disconnection

I have the following complete program (which can be copy-pasted-built-and-run. you might have to add a few references). 我有以下完整的程序(可以复制粘贴,构建并运行。您可能需要添加一些引用)。 The objective of the program is for the service to detect (eg receive a SocketException or IOException of some form or as attempted in the code via some event handling) that a connected client (tested/test from web-browser) has disconnected before the response has been fully delivered (see return statements in method Talk(string animal) ). 该程序的目的是使服务在响应之前检测到(例如,通过某种事件处理接收到某种形式的SocketExceptionIOException或在代码中通过某些事件处理所尝试的)已断开连接的客户端(从Web浏览器测试/测试)已断开连接已完全交付(请参见Talk(string animal)方法中的return语句)。 To reproduce the issue, there is a configurable parameter (see new AnimalTalkService(3) ) which dictates how long the service will take to respond to a given request. 为了重现此问题,有一个可配置的参数(请参阅new AnimalTalkService(3) ),该参数指示服务响应给定请求将花费多长时间。 And within this time frame, i can close the browser in order to raise the client-disconnection event (see method ClientDisconnected() in class ClientConnectionTracker ). 在此时间范围内,我可以关闭浏览器以引发客户端断开连接事件(请参阅类ClientConnectionTracker方法ClientDisconnected() )。 I am unable to get any exceptions thrown into the implementation of the service or get the Closed and Faulted events triggered. 我无法将任何异常抛出到服务的实现中,也无法触发ClosedFaulted事件。 Would someone have an idea on how to go about to (implement) get the desired effect? 有人会对如何实现(实现)达到预期的效果有想法吗?

// Code:

using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Threading;

namespace TestClientDisconnect
{
    class ClientConnectionTracker : IChannelInitializer
    {
        public void Initialize(IClientChannel channel)
        {
            channel.Closed += ClientDisconnected;
            channel.Faulted += ClientDisconnected;
        }

        private void ClientDisconnected(object sender, EventArgs e)
        {
            Console.WriteLine("Client Disconnected");
            throw new NotImplementedException();
        }
    }

    class ClientConnectionTrackerEndpointBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.ChannelDispatcher.ChannelInitializers.Add(new ClientConnectionTracker());
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }

    [ServiceContract]
    interface IAnimalTalkService
    {
        [OperationContract]
        [WebInvoke(UriTemplate = "/{animal}", Method = "GET")]
        string Talk(string animal);
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    class AnimalTalkService : IAnimalTalkService
    {
        private int delayInSeconds;

        public AnimalTalkService(int delayInSeconds = 0)
        {
            this.delayInSeconds = delayInSeconds;
        }

        public string Talk(string animal)
        {
            Console.WriteLine("Creating sentence for animal {0} ...", animal);
            if (delayInSeconds > 0)
            {
                // Simulate heavy duty work:
                Thread.Sleep(1000 * delayInSeconds);
            }

            switch(animal.ToLower())
            {
                case "sheep":
                    return "baa";
                case "dog":
                    return "woof";
                case "cat":
                    return "miao";
                default:
                    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
                    return null;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            AnimalTalkService serviceInstance = new AnimalTalkService(3);
            Uri address = new Uri("http://127.0.0.1:1234/");
            WebServiceHost host = new WebServiceHost(serviceInstance, address);
            WebHttpBinding binding = new WebHttpBinding();
            ServiceEndpoint endPoint = host.AddServiceEndpoint(typeof(IAnimalTalkService), binding, "");
            endPoint.EndpointBehaviors.Add(new WebHttpBehavior() { DefaultOutgoingResponseFormat = WebMessageFormat.Json });
            endPoint.EndpointBehaviors.Add(new ClientConnectionTrackerEndpointBehavior());
            host.Open();

            Console.WriteLine("Service is running at {0}. Press Enter key to exit", host.BaseAddresses[0]);
            Console.ReadLine();
        }
    }
}

Thanks in advance. 提前致谢。

The word "disconnection" implies a session, isn't it? “断开连接”一词意味着一个会话,不是吗?

The best way, in my opinion, to have methods, that explicitly creates and terminates the session using an explicit session id (here I used an arbitrary type): 我认为,最好的方法是使用一个显式的会话ID(在这里我使用任意类型)来显式创建和终止会话:

[ServiceContract]
public interface IWebService
{
    [OperationContract]
    SessionId BeginNewSession();
    [OperationContract]
    void DoSomething(SessionId id, ...);
    [OperationContract]
    void EndSession(SessionId id);
}

This is certainly recommended for HTTP protocol, which doesn't support transport-level sessions. 对于不支持传输级会话的HTTP协议,当然推荐这样做。

In this case you can write another class, that will housekeep outdated sessions which haven't been closed. 在这种情况下,您可以编写另一个类,该类将保留尚未关闭的过时会话。

In case you use a binding that supports transport-level sessions, there is another option - to set up session-bound service instance management (and use corresponding binding), implement IDisposable interface in the service class and place the related code inside Dispose() method: 如果使用支持传输级会话的绑定,则还有另一个选择-设置会话绑定的服务实例管理(并使用相应的绑定),在服务类中实现IDisposable接口,并将相关代码放入Dispose()方法:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TheService : IService, IDisposable
{
    ...
    public void Dispose()
    {
        // code of session termination
        ...
    }
}

Finally you can combine both options by marking the explicit session-terminating method with [OperationContract(IsTerminating = true)] attribute: 最后,您可以通过使用[OperationContract(IsTerminating = true)]属性标记显式会话终止方法来组合这两个选项:

[ServiceContract(..., SessionMode=SessionMode.Required)]
public interface IService
{
    [OperationContract(IsTerminating = true)]
    void Close();
...
}

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

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