2017-04-17 102 views
0

我有以下完整的程序(可以複製 - 粘貼 - 構建並運行,您可能需要添加幾個引用)。該程序的目的是使服務能夠檢測(例如,通過某些事件處理接收某種形式的或IOException或者通過某些事件處理在代碼中嘗試),在響應之前,連接的客戶端(來自網絡瀏覽器的測試/測試)已完全交付(請參見方法Talk(string animal)中的return陳述)。要重現此問題,有一個可配置的參數(請參閱new AnimalTalkService(3)),該參數決定了服務響應給定請求需要多長時間。在此時間範圍內,我可以關閉瀏覽器以提高客戶端斷開連接事件(請參見類ClientConnectionTracker中的方法ClientDisconnected())。我無法獲得任何例外情況投入到服務的實施中,或者觸發事件觸發事件ClosedFaulted。會有人有一個想法如何去(實現)獲得預期的效果?WCF服務如何檢測客戶端斷開

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

在此先感謝。

+0

關閉瀏覽器不會消防客戶端斷開連接事件。這隻會在你調用((ICommunicationObject)client.CallbackChannel).Close();方法明確從您的客戶。 – Vasanthan

+0

@Vasanthan:在客戶端沒有正常終止的情況下應調用Faulted。 –

回答

0

「斷開」一詞意味着一個會話,不是嗎?

的最佳方式,在我看來,有方法,明確創建和使用一個明確的會話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(); 
... 
} 
相關問題