简体   繁体   中英

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) ). 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. And within this time frame, i can close the browser in order to raise the client-disconnection event (see method ClientDisconnected() in class ClientConnectionTracker ). I am unable to get any exceptions thrown into the implementation of the service or get the Closed and Faulted events triggered. 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):

[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.

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:

[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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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