• WCF热门问题编程示例(5):WCF服务如何获取客户端在线用户数量?


    WCF热门问题编程示例(5):WCF服务如何获取客户端在线用户数量?

    这个问题是基于WCF学习交流群的Blood提出的问题,一起讨论了下,做的测试。把相关的讨论,以及测试代码,整理成一篇博文。

    【1】问题分析:

         这个问题,在WCF服务编程中也非常的常见,以下是对于这个问题的不同描述形式,但是本质基本类似:

    1. WCF如何获取在线客户端数量?
    2. WCF如何获取在线用户列表?
    3. WCF服务如何知道客户端离线?
    4. 如何判断WCF离线客户端?

        或许还有别的提法,但是基本都是差不多的。

        此类问题出现在回调、双工通信的场景中比较多,有的程序具备类似聊天室的功能,就比较在乎客户端的离线事件。

    【2】解决办法:

        这里服务端对于客户端在线的判断,也是固定的,基本是基于对通道状态的判断,来实现对于客户端在线状态的判断。

    实现的思路基本就是,在服务端维护一个在线客户端Channel的列表List,然后每次通道关闭(Closed)或者出错(Faulted)调用特定的方法来移调通道。

         这里另外一个需要注意的地方就是多线程并发的问题。

        因为在线用户通道list是一个静态变量,多线程访问的时候,需要注意互斥操作的问题。

    【3】示例代码:

          这里的代码页比较简单,基本思路:

    //回调通道列表,也可以用来保持
    private static List<IWCFServiceCallBack> channelsList = new List<IWCFServiceCallBack>();
    private  static Object thisLock = new Object();

         另外服务默认的是PerSession实例模式,对于单个客户端Proxy实例只有一个服务。

    【3.1】服务端:

         这里最重要的就是一个绑定一个方法给Closed事件,

    OperationContext.Current.Channel.Closed += new EventHandler(ShowOffLine);

       全部的代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.Runtime.Serialization;
    using System.Threading;
    //ServiceContract 属性以及 Indigo 使用的所有其他属性均在 System.ServiceModel 命名空间中定义,
    //因此本例开头使用 using 语句来引用该命名空间。
    namespace WCFService
    {
        //1.回调服务契约,由于回调方法在客户端执行,因此无须添加 ServiceContractAttribute。对于回调操作,服务器无须获取其返回信息,因此添加 IsOneWay=true 特性参数。
        public interface IWCFServiceCallBack
        {
            //操作契约
            [OperationContract(IsOneWay=true)]//
            void SayHelloCalllBack();
        }
        //2.服务契约,指定 CallbackContract 回调契约。
        [ServiceContract(CallbackContract = typeof(IWCFServiceCallBack))]
        public interface IWCFService
        {
            //操作契约,
            [OperationContract]
            string SayHelloToUser(string name);

        }
        //3.服务类,继承接口。实现服务契约定义的操作
        public class WCFService : IWCFService
        {
            //回调通道列表,也可以用来保持
            private static List<IWCFServiceCallBack> channelsList = new List<IWCFServiceCallBack>();
            private  static Object thisLock = new Object();

            public WCFService()
            {
                Console.WriteLine("constructed! {0} , …", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
            }
            //实现接口定义的方法
            public string SayHelloToUser(string name)
            {
                Console.WriteLine("Begin Call at  {0} , …", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                //获取当前操作客户端对象实例
                IWCFServiceCallBack callback = OperationContext.Current.GetCallbackChannel<IWCFServiceCallBack>();
                //绑定事件方法
                OperationContext.Current.Channel.Closed += new EventHandler(ShowOffLine);
                //OperationContext.Current.Channel.Faulted += new EventHandler(ShowOffLine);
                //添加回调通道
                lock (thisLock)
                {
                    channelsList.Add(callback);
                }
                callback.SayHelloCalllBack();
                Console.WriteLine("End Call at {0} , …", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                return "Hello! " + name;
            }

            private void ShowOffLine(object sender, EventArgs e)
            {
                IWCFServiceCallBack callback = sender as IWCFServiceCallBack;
                lock (thisLock)
                {
                    channelsList.Remove(callback);
                    Console.WriteLine("OffLine! {0} , current clients are {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), channelsList.Count);
                }
            }
        }
    }

    【3.2】客户端:

          客户端实例化了10个Proxy的实例,调用10次服务,服务回调客户端。我们直接结束客户端的进程,观察一下Host窗口的输出信息。代码很简单,如下:

    //CallBack 回调服务
               Console.WriteLine("Call Back Operation Test…");
               for (int i = 0;i < 10;i++)
               {
                   IWCFServiceCallback callBack = new WCFServiceCallback();
                   InstanceContext context = new InstanceContext(callBack);
                   WCFServiceClient WCFServiceCallBackClientProxy = new WCFServiceClient(context, "NetTcpBinding_IWCFService");
                   //通过代理调用调用SayHelloToUser,传递对象
                   WCFServiceCallBackClientProxy.SayHelloToUser("Frank Xu Lei Call Back");
               }
               //For Debug
               Console.WriteLine("Press any key to continue…");
               Console.Read();

        

    【4】运行结果:

           这里我们运行了10个客户端,然后分别关闭10个客户端,可以看到Host窗口打印的在线用户数目在发生变化,依次减少。

    HowToGetOnLineClientNum

    这里为什么没绑定Faulted 事件上呢?//OperationContext.Current.Channel.Faulted += new EventHandler(ShowOffLine);

    而是只保留了对于Closed事件的绑定?

    主要原因是,WCF的通道状态机,在通道错误的时候,最后的状态也会转换为CLosed,所以绑定到Closed事件一定会调用我们的ShowOffline方法。

    【5】参考文章:

    1. Handling Disconnects with WCFhttp://www.rcs-solutions.com/blog/2008/09/24/HandlingDisconnectsWithWCF.aspx
    2. 线程同步:http://msdn.microsoft.com/zh-cn/library/ms173179(VS.80).aspx
  • 相关阅读:
    生产者消费者模型
    varchar2存储汉字,英文字符,数字在oracle中的多少
    正则表达式以及邮箱
    爬虫
    创建git本地仓库和GitHub远程仓库并配置连接的从无到有
    CSSHTML实现高度宽度自适应
    实现一个元素在当前窗口垂直水平居中的几种方法
    angular4 rxjs 异步处理多个http请求数据
    angular 4 父子组件异步交互
    同步异步单线程多线程初级理解
  • 原文地址:https://www.cnblogs.com/cpcpc/p/2123058.html
Copyright © 2020-2023  润新知