传说中的WCF(11):会话(Session)



http://blog.csdn.net/tcjiaan/article/details/8281782


在标题中我加了一个大家都很熟悉的单词——Session,熟吧?玩过Web开发的朋友肯定在梦中都会见到她。

在Web中为什么要会话呢?毕竟每个用户在一个Web应用中可能不止进行一次操作,比如,某二手飞机交易网站,用户A登陆后,可能他会修改他的个人信息,他也有可能看好了一架二手飞机,打算入手,就把商品放到他的“购物车”中,这些过程中,都会产生许多与用户A相关的数据,这些数据只是对A有效,而当用户B登陆后,对于B,又会有他自己的数据,总的一句话就是,每个客户端在服务器上都有其的独立数据存储区,互不相干,就好像A和服务器在单独谈话一样,所以叫会话

在WCF中,会话的含义与Web中的会话概念是差不多的,就是客户端与服务器端在“私聊”,这便是存在会话的调用;那么,没有会话的调用呢,就是“群聊”;通信过程中,数据都以明文显示,不进行加密保护,这叫“裸聊”。

好了,现在大家对于会话,肯定有点理解了。但是,会话是看不见摸不着的,怎么通过实例来检验它呢?

 下面,我们写一个例子,看看在不支持会话的绑定上连续调用两个有关联的代码,会发生什么情况。

[csharp]  view plain copy print ?
  1. [ServiceContract]  
  2. public interface IService  
  3. {  
  4.     [OperationContract(IsOneWay = true)]  
  5.     void SetValue(int n);  
  6.     [OperationContract]  
  7.     int GetValue();  
  8. }  
  9.   
  10. [ServiceBehavior(IncludeExceptionDetailInFaults = true)]  
  11. public class MyService : IService  
  12. {  
  13.   
  14.     public MyService()  
  15.     {  
  16.         Console.WriteLine("-------------------------------");  
  17.         Console.WriteLine("{0} - 服务被实例化。",DateTime.Now.ToLongTimeString());  
  18.     }  
  19.     // 在析构函数中也输出信息  
  20.     ~MyService()  
  21.     {  
  22.         Console.WriteLine("{0} - 服务实例被释放。", DateTime.Now.ToLongTimeString());  
  23.         Console.WriteLine("--------------------------------");  
  24.     }  
  25.   
  26.     /// <summary>  
  27.     /// 私有字段  
  28.     /// </summary>  
  29.     private int m_Value = int.MinValue;  
  30.   
  31.     public void SetValue(int n)  
  32.     {  
  33.         this.m_Value = n;  
  34.         Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);  
  35.     }  
  36.   
  37.     public int GetValue()  
  38.     {  
  39.         Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);  
  40.         return this.m_Value;  
  41.     }  
  42. }  


在服务类中,我们分别在构造函数和析构函数中输出一些内容,以便于在运行时看到服务什么时候被实例化,什么时候被释放。同时,在调用每个操作协定时,我们也输出当前会话的ID,如果空就说明当前没有启用会话。

接着,实现服务主机,我们使用不支持的Bindding。

[csharp]  view plain copy print ?
  1. static void Main(string[] args)  
  2. {  
  3.     Console.Title = "WCF服务器端";  
  4.     using (ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://127.0.0.1:1211/sv")))  
  5.     {  
  6.         // 绑定  
  7.         BasicHttpBinding binding = new BasicHttpBinding();  
  8.         binding.Security.Mode = BasicHttpSecurityMode.None;//不需要安全模式  
  9.         host.AddServiceEndpoint(typeof(IService), binding, "/ep");  
  10.         // 服务元数据  
  11.         ServiceMetadataBehavior mb = new ServiceMetadataBehavior();  
  12.         mb.HttpGetEnabled = true;  
  13.         mb.HttpGetUrl = new Uri("http://127.0.0.1:8008/meta");  
  14.         host.Description.Behaviors.Add(mb);  
  15.         host.Opened += (sender, arg) =>  
  16.             {  
  17.                 Console.WriteLine("服务已启动。");  
  18.             };  
  19.         try  
  20.         {  
  21.             host.Open();//打开服务  
  22.             Console.Read();  
  23.         }  
  24.         catch (Exception ex)  
  25.         {  
  26.             Console.WriteLine(ex.Message);  
  27.         }  
  28.     }  
  29. }  


然后,实现客户端,为了使演示操作更方便,客户端使用Windows Forms项目,界面大致如下图所示,源代码我后面会上传到资源。

 

服务有两个操作,从前面服务类的定义我们知道,我在服务类内部定义了一个私有字段,而公开的两个操作协定,分别是设置这个值和获取这个值。

[csharp]  view plain copy print ?
  1. public partial class Form1 : Form  
  2. {  
  3.     WS.ServiceClient client = null;  
  4.     public Form1()  
  5.     {  
  6.         InitializeComponent();  
  7.         client = new WS.ServiceClient();  
  8.         // 关闭通道  
  9.         this.FormClosing += (frmsender, frmargs) => client.Close();  
  10.     }  
  11.   
  12.     private void btnCall1_Click(object sender, EventArgs e)  
  13.     {  
  14.         int v;  
  15.         if (!int.TryParse(this.txtInput.Text, out v))  
  16.         {  
  17.             return;  
  18.         }  
  19.         client.SetValue(v);  
  20.     }  
  21.   
  22.     private void btnCall2_Click(object sender, EventArgs e)  
  23.     {  
  24.         int v = client.GetValue();  
  25.         this.txtOutput.Text = v.ToString();  
  26.     }  
  27. }  


我们现在要测试一下,看看先后调用这两个方法,能不能得到我们预期的值。即如果我调用SetValue(100),那么,在调用GetValue时应该返回100,事实是不是这样呢?

八格牙路,我没有看到预期的值。

 我输入了100,可是取出来的是Min int,再看一看服务器端输出了哪些信息。

 很明显,每调用一次操作,服务类就被实例化一次,意思就是:调用SetValue时是实例A,而调用GetValue时可能是实例B了,所以,私有字段的值没有被保存。

那么,如何证明两次调用的操作不属于同一个服务实例呢?还记得GetHashCode吗?对的,只要在内存中不是同一个实例的,其哈希值肯定不同。是不是这样呢,我们把上面的服务代码改一下。

[csharp]  view plain copy print ?
  1. public void SetValue(int n)  
  2. {  
  3.     this.m_Value = n;  
  4.     Console.WriteLine("------------------------");  
  5.     Console.WriteLine("当前实例的哈希值:{0}"this.GetHashCode().ToString("x"));  
  6.     Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);  
  7. }  
  8.   
  9. public int GetValue()  
  10. {  
  11.     Console.WriteLine("------------------------");  
  12.     Console.WriteLine("当前实例的哈希值:{0}"this.GetHashCode().ToString("x"));  
  13.     Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);  
  14.     return this.m_Value;  
  15. }  

那么,这次又会发生什么事呢,看结果。


这个结果证实了我之前的推断,先后调用的两个方法不是同一个实例的。

 

 

 那么,如果启用了会话,结果又会如何呢?

[csharp]  view plain copy print ?
  1. [ServiceContract(SessionMode = SessionMode.Required)]  
  2. public interface IService  
  3. {  
  4.     。。。。。  
  5.  }  

在定义服务协定的时候,设置SessionMode可以让服务要求客户端启用会话。

接下来,要使用支持会话的绑定。

[csharp]  view plain copy print ?
  1. // 绑定  
  2. //BasicHttpBinding binding = new BasicHttpBinding();  
  3. //binding.Security.Mode = BasicHttpSecurityMode.None;//不需要安全模式  
  4. //host.AddServiceEndpoint(typeof(IService), binding, "/ep");  
  5. NetTcpBinding binding = new NetTcpBinding();  
  6. binding.Security.Mode = SecurityMode.None;  
  7. host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://127.0.0.1:2377/ep");  

 

把客户端的服务引用更新一下。然后看看这回会不会达到我们的目的。

 


怎么样,高兴吧?终于看到我们要的效果了,我输入了100,取出来的还是100,这一回从服务器端的输出看,服务类只被实例化了一次,而且看看两个哈希值是相同的,这证明了确实是同一个实例,同时,我们也看到了两次调用的会话ID是一样的,这也说明了,客户端两次调用都基于同一个会话进行的,这么一来,输进去的100就能顺利取出来了。

你不妨多开几个客户端来试试。

 

 

 看到那个不同的会话ID,哈希值和实例化时间了吧?这表明了:服务器独立维护着与每个客户端的会话

 

下面还要对我们的解决方案进行修改。

服务器端,把服务协定改为:

[csharp]  view plain copy print ?
  1. [ServiceContract(SessionMode = SessionMode.Required)]  
  2. public interface IService  
  3. {  
  4.     [OperationContract(IsOneWay = true,IsInitiating = true, IsTerminating = false)]  
  5.     void SetValue(int n);  
  6.     [OperationContract]  
  7.     int GetValue();  
  8.     [OperationContract(IsInitiating = false, IsTerminating = true)]  
  9.     void EndSession();  
  10. }  

在服务类中增加对EndSession方法的实现。

[csharp]  view plain copy print ?
  1. public void EndSession()  
  2. {  
  3.     Console.WriteLine("会话结束。");  
  4. }  

看到变化了吗?

我们在使用OperationContractAttribute定义操作协定时,设置了两个属性:

a、IsInitiating:如果为真,则当调用该操作时就启用会话。

b、IsTerminating:如果为真,则说明当该用该操作时终结会话。

所以,上面的例子是,当调用SetValue时开始会话,当调用EndSession方法后会话结束,在选择作为结束会话的方法时,最好使用返回值为void或者单向通讯(One Way)的方法,这样,不用等待客户结束才结束会话,因为单向通讯,不需要向客户端回复消息,因为它被调用后就可以马上终止会话了。

 

在客户端代码中,我们要取消之前的关闭通道的代码,因为不再需要,会话一旦终结,通道就自动关闭,服务实例就会自动人间消失。

[csharp]  view plain copy print ?
  1. WS.ServiceClient client = null;  
  2. public Form1()  
  3. {  
  4.     InitializeComponent();  
  5.     client = new WS.ServiceClient();  
  6.     // 关闭通道  
  7.     //this.FormClosing += (frmsender, frmargs) => client.Close();  
  8. }  


然后,在调用完GetValue后马上就EndSession,看看这回又会发生什么。



这样,一旦三个方法调用完之后,会话结束,服务实例也解放了。

再说明一下,IsInitiating = true的操作开始新会话,IsTerminating = true的操作终结会话


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值