簡體   English   中英

WCF服務如何檢測客戶端斷開連接

[英]How can WCF service detect client disconnection

我有以下完整的程序(可以復制粘貼,構建並運行。您可能需要添加一些引用)。 該程序的目的是使服務在響應之前檢測到(例如,通過某種事件處理接收到某種形式的SocketExceptionIOException或在代碼中通過某些事件處理所嘗試的)已斷開連接的客戶端(從Web瀏覽器測試/測試)已斷開連接已完全交付(請參見Talk(string animal)方法中的return語句)。 為了重現此問題,有一個可配置的參數(請參閱new AnimalTalkService(3) ),該參數指示服務響應給定請求將花費多長時間。 在此時間范圍內,我可以關閉瀏覽器以引發客戶端斷開連接事件(請參閱類ClientConnectionTracker方法ClientDisconnected() )。 我無法將任何異常拋出到服務的實現中,也無法觸發ClosedFaulted事件。 有人會對如何實現(實現)達到預期的效果有想法嗎?

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

提前致謝。

“斷開連接”一詞意味着一個會話,不是嗎?

我認為,最好的方法是使用一個顯式的會話ID(在這里我使用任意類型)來顯式創建和終止會話:

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

對於不支持傳輸級會話的HTTP協議,當然推薦這樣做。

在這種情況下,您可以編寫另一個類,該類將保留尚未關閉的過時會話。

如果使用支持傳輸級會話的綁定,則還有另一個選擇-設置會話綁定的服務實例管理(並使用相應的綁定),在服務類中實現IDisposable接口,並將相關代碼放入Dispose()方法:

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

最后,您可以通過使用[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