详解如何获取客户端使用WCF服务的用户数,通过WCF取在线用户数量的方法

本文和大家讲下WCF服务如何获取客户端在线用户数量?你没有关注过这个问题吧,一起来看下本文的实现方法。
【1】问题分析:
     这个问题,在WCF服务编程中也非常的常见,以下是对于这个问题的不同描述形式,但是本质基本类似:
WCF如何获取在线客户端数量?
WCF如何获取在线用户列表?
WCF服务如何知道客户端离线?
如何判断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窗口打印的在线用户数目在发生变化,依次减少。
 
这里为什么没绑定Faulted 事件上呢?//OperationContext.Current.Channel.Faulted += new EventHandler(ShowOffLine);
而是只保留了对于Closed事件的绑定?
主要原因是,WCF的通道状态机,在通道错误的时候,最后的状态也会转换为CLosed,所以绑定到Closed事件一定会调用我们的ShowOffline方法。


 

 

posted on 2010-08-28 15:25  xufeng001  阅读(759)  评论(0编辑  收藏  举报

导航