2017-04-17 105 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(); 
... 
} 
相关问题