当前位置: 首页 > news >正文

细说WCF中的会话模式

大家都知道WCF会话模式有几个要求:1、会话契约;2、绑定支持;3、实例模式为PerSession。这几个要素是WCF支持的必要条件。

 

  • 会话契约:由服务端提供实现,客户端调用时只持有契约定义,所以需要通过契约定义告知客户端,服务端是支持会话的。
  • 绑定:会话没有绑定的支持也就无从谈起了。
  • InstanceContextMode为PerSession。通过它可以保证在会话期间,服务实例不会被销毁。

  较为复杂的问题在于ServiceContract中SessionMode的设置。SessionMode定义如下:

 

   //  摘要:
    
//      指定可用于指示支持协定需要或支持的可靠会话的值。
     public  enum SessionMode
    {
         //  摘要:
        
//      指定当传入绑定支持会话时,协定也支持会话。
        Allowed =  0,
         //
        
//  摘要:
        
//      指定协定需要会话绑定。如果绑定并未配置为支持会话,则将引发异常。
        Required =  1,
         //
        
//  摘要:
        
//      指定协定永不支持启动会话的绑定。
        NotAllowed =  2,
    }

   

通过以上SessionMode的枚举定义可知:Required肯定是强制启用会话;NotAllowed强制不之处会话;Allowed允许启用会话。最麻烦的要数Allowed。首先Allowed是他是支持会话的,其次:它允许并不意味着客户端与服务端的信息交互一定是会话模式的。那么在什么情况下我们将契约定义为允许会话,服务端与客户端之间的通讯是会话模式?什么情况下又是非会话模式呢?会话模式与可靠会话之间有没有什么关系?在启用会话模式时,客户端的SessionId与服务端的SessionId一定是完全匹配的吗?带着这些问题,来进行一些说明。

1、绑定 

先从绑定说起:basicHttpBinding它不能在信息头中嵌入SessionId,客户端与服务端之间基于Http的通讯也就不可能维持会话了;Msmq模式下,服务端与客户端可以是基于离线模式的,因此也不支持会话。对于Tcp类型的绑定,如NetTcpBinding与NetNamedPipeBinding由于使用的是带连接的Tcp作为传输协议,所以它是能支持会话的。

2、SessionMode下的SessionId 

 可以通过OperationContext的SessionId属性来访问SessionId。

服务端访问SessionId:

OperationContext.Current.SessionId

 

在客户端,需要先初始化OperationContextScope,后才能访问SessionId。如下:

       IContextChannel contextChannel = proxy  as IContextChannel;      
                using (OperationContextScope contextScope =  new OperationContextScope(contextChannel))
               {                  
                   string sessionId=OperationContext.Current.SessionId);
               }


  3、SessionMode=SessionMode.Allowed下服务端与客户端的SessionId匹配程度

讨论之前,先给出契约以及服务实现。如下:

    [ServiceContract(SessionMode = SessionMode.Allowed)]
     public  interface IAdd
    {
        [OperationContract]        
         int Add( int x, int y);
    }
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
     public  class AddService : IAdd,IDisposable
    {

         #region IAdd Members

         public  int Add( int x,  int y)
        {
             if ( null != OperationContext.Current.SessionId)
            {
                Console.WriteLine( " SessionId is {0} ", OperationContext.Current.SessionId);
            }
             else
            {
                Console.WriteLine( " SessionId is null ");
            }            
             return x + y;
        }

         #endregion

         public  void Dispose()
        {
            Console.WriteLine( " Dispose Thread Id is {0} ",System.Threading.Thread.CurrentThread.ManagedThreadId);
        }
    }


3.1、NetTcpBinding的会话 

3.1.1、启用可靠会话

在配置文件中进行配置即可。如下:

 

调用之后访问SessionId:

    using (ChannelFactory<IAdd> channelFactory =  new ChannelFactory<IAdd>( " tcp "))
            {
                IAdd proxy = channelFactory.CreateChannel();
                 Console.WriteLine( " result is {0} " , proxy.Add( 1 2 ));
                IContextChannel contextChannel = proxy  as IContextChannel;
                 using (OperationContextScope contextScope =  new OperationContextScope(contextChannel))
                {
                    Console.WriteLine( " operationContextScope hashcode is {0} ", contextScope.GetHashCode());
                     if ( null == OperationContext.Current.SessionId)
                    {
                        Console.WriteLine( " SessionId is null ");
                    }
                     else
                    {
                         Console.WriteLine( " Session is {0} " , OperationContext.Current.SessionId);
                    }
                }
                
            }

服务端输出:

 

 

客户端输出:

   

 

在客户端调用之前获取SessionId:

服务端输出如下:

 

 

客户端输出如下: 

 

如果在调用服务之前,手动打开代理,情况怎样呢?

将客户端的代码做如下修改:

  IAdd proxy = channelFactory.CreateChannel();
                (proxy  as  ICommunicationObject).Open();
                IContextChannel contextChannel = proxy  as IContextChannel;
                 using (OperationContextScope contextScope =  new OperationContextScope(contextChannel))
                {
                    Console.WriteLine( " operationContextScope hashcode is {0} ", contextScope.GetHashCode());
                     if ( null == OperationContext.Current.SessionId)
                    {
                        Console.WriteLine( " SessionId is null ");
                    }
                     else
                    {
                        Console.WriteLine( " Session is {0} ", OperationContext.Current.SessionId);
                    }
                }
                Console.WriteLine( " result is {0} ", proxy.Add( 12));

服务端输出: 

 

 

客户端输出:

 

 

3.1.2 禁用可靠会话(NetTcpBinding默认)

服务端输出:


 

客户端输出:


 

  小结:在NetTcpBinding绑定下,如果启用可靠会话传输,则服务端与客户端的SessionId是相同的。如果禁用可靠会话,则两者SessionId是不一样的。另外,在进行第一次调用前,客户端获取不到SessionId。除非在调用之前手动打开代理。

 

 3.2、ws*绑定

在ws-*绑定中,WCF通过在消息头中加入SessionId,通过SessionId来识别客户端(准确来说是代理)。以下使用WsHttpBinding进行说明。

  3.2.1、禁用可靠会话(调用之前手动打开代理)

服务端输出:

             

客户端输出:

        

 

3.2.2、启用可靠会话

服务端输出:

 

客户端输出:


 

   

 3.2.3、禁用会话(不手工打开代理,调用之前获取SessionId)

服务端输出:

 

   小结可靠会话对客户端SessionId有影响。在开启可靠会话时,如客户端在调用之前手动打开代理,则客户端与服务端的SessionId相同;如果调用之前不手动打开代理,则客户端获取不到SessionId,只有在第一次调用后才能获取到SessionId;在禁用可靠会话时,客户端在不手动打开代理的情况下调用服务会发生异常。

 

另外说明:使用Tcp作为传输协议,通过三次握手,它通过超时、丢包重传的机制保证客户端到服务端的消息传输成功,但与WCF中消息的可靠会话是有本质区别的。WCF中通过自身机制保证从RM源到目标源消息的发送以及确认机制、以及服务端中消息从目标源到最终交付对象之间消息的交付机制。使用NetTcpBinding时,我们可以通过它的默认构造函数看看构成的绑定元素有哪些。如下代码:


    var binding =  new NetTcpBinding();
            BindingElementCollection collection = binding.CreateBindingElements();
             foreach (BindingElement bindingElement  in collection)
            {
                Console.WriteLine(bindingElement.GetType());
            }
            Console.WriteLine( " --------使用构造函数,设置NetTcpBinding允许可靠会话-------- ");
             var binding2 =  new NetTcpBinding(SecurityMode.None, true);
            BindingElementCollection collection2 = binding2.CreateBindingElements();
             foreach (BindingElement bindingElement  in collection2)
            {
                Console.WriteLine(bindingElement.GetType());
            }

输出如下:

 

上图中ReliableSessionBindingElement就是创建可开会话信道的。

 

总结:不同类型的协议对于SessionId的获取是有影响的。无论对于TCP协议还是ws-* 协议,如果客户端调用之前不手动打开代理,则调用之前客户端是获取不到SessionId的;在进行第一次调用之后,客户端才能获取到与服务端相同的SessionId(因为进行调用时,会自动打开代理).
是否启用会话也会影响到SessionId。对于TCP协议来说,禁用可靠会话,客户端获取到SessionId与服务端是不一样的;对于ws-* 协议而言:如果禁用可靠会话,调用之前如果不手动代开代理,则调用会发生异常。

  

相关文章:

  • 回声状态网络(ESN)基础教程
  • C# 序列化
  • VisualSVN 手动记录访问日志
  • JDBC(连接池) -- 02(I)
  • windows   8   OneNoteMX
  • 第二次作业-Steam软件分析
  • [面试] 组合(非递归)
  • Which garbage collection strategy is using
  • OutputCache造成页面响应内容类型为text/vnd.wap.wml的问题
  • windws 8 应用小技巧(11-15)
  • Mac禁用ipv6
  • C语言程序设计第一次作业
  • 学习PrintWriter类
  • 物联网设备漏洞不断增加 五大安全层面随时检视
  • 从ORACLE转战虚拟化 与VMware展开肉搏战来看
  • [译] React v16.8: 含有Hooks的版本
  • angular组件开发
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • ES6系统学习----从Apollo Client看解构赋值
  • Flannel解读
  • Java到底能干嘛?
  • Leetcode 27 Remove Element
  • MYSQL 的 IF 函数
  • Sass 快速入门教程
  • session共享问题解决方案
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 分布式熔断降级平台aegis
  • 简单数学运算程序(不定期更新)
  • 容器服务kubernetes弹性伸缩高级用法
  • 如何学习JavaEE,项目又该如何做?
  • 小程序01:wepy框架整合iview webapp UI
  • Prometheus VS InfluxDB
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • ​ubuntu下安装kvm虚拟机
  • # Java NIO(一)FileChannel
  • #Ubuntu(修改root信息)
  • %@ page import=%的用法
  • (3)nginx 配置(nginx.conf)
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (四)Controller接口控制器详解(三)
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • (轉)JSON.stringify 语法实例讲解
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET Framework .NET Core与 .NET 的区别
  • .NET 材料检测系统崩溃分析
  • .NET 反射 Reflect
  • .net连接MySQL的方法
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • @angular/cli项目构建--http(2)
  • @我的前任是个极品 微博分析
  • [2019/05/17]解决springboot测试List接口时JSON传参异常