zoukankan      html  css  js  c++  java
  • 细说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(12));
                    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-* 协议而言:如果禁用可靠会话,调用之前如果不手动代开代理,则调用会发生异常。

      

     
    分类: WCF基础
  • 相关阅读:
    usaco 1.3.1 Mixing Milk
    ACM必备(学完一个就加亮一个)不多,就这些!
    usaco1.2.4Palindromic Squares
    usaco 1.2.3 Name That Number
    coursera机器学习笔记机器学习概论,梯度下降法
    coursera机器学习笔记多元线性回归,normal equation
    coursera机器学习支持向量机SVM
    使用C/C++,赋值运算时发生的转换
    coursera机器学习logistic回归,正则化
    coursera机器学习笔记神经网络,初识篇
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2725216.html
Copyright © 2011-2022 走看看