【问题标题】:How can WCF service detect client disconnectionWCF服务如何检测客户端断开连接
【发布时间】:2017-04-17 11:09:31
【问题描述】:

我有以下完整的程序(可以复制粘贴构建并运行。您可能需要添加一些参考)。该程序的目标是让服务检测(例如接收某种形式的SocketExceptionIOException 或通过某些事件处理在代码中尝试)连接的客户端(从网络浏览器测试/测试)具有在响应完全传递之前断开连接(参见方法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();
        }
    }
}

提前致谢。

【问题讨论】:

  • 关闭浏览器不会触发客户端断开事件。这仅在您调用 ((ICommunicationObject)client.CallbackChannel).Close(); 时有效来自您的客户的明确方法。
  • @Vasanthan:如果客户端没有正常终止,则应调用 Faulted。

标签: c# wcf http networking error-handling


【解决方案1】:

“断开”一词意味着会话,不是吗?

在我看来,最好的方法是拥有使用显式会话 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();
...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 2014-04-14
    相关资源
    最近更新 更多