zoukankan      html  css  js  c++  java
  • 重温WCF之会话Session(九)

    转载地址:http://blog.csdn.net/tcjiaan/article/details/8281782

    每个客户端在服务器上都有其的独立数据存储区,互不相干,就好像A和服务器在单独谈话一样,所以叫会话

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

        [ServiceContract]
        public interface IService
        {
            [OperationContract(IsOneWay = true)]
            void SetValue(int n);
            [OperationContract]
            int GetValue();
        }
    
        [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
        public class MyService : IService
        {
    
            public MyService()
            {
                Console.WriteLine("-------------------------------");
                Console.WriteLine("{0} - 服务被实例化。",DateTime.Now.ToLongTimeString());
            }
            // 在析构函数中也输出信息
            ~MyService()
            {
                Console.WriteLine("{0} - 服务实例被释放。", DateTime.Now.ToLongTimeString());
                Console.WriteLine("--------------------------------");
            }
    
            /// <summary>
            /// 私有字段
            /// </summary>
            private int m_Value = int.MinValue;
    
            public void SetValue(int n)
            {
                this.m_Value = n;
                Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);
            }
    
            public int GetValue()
            {
                Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);
                return this.m_Value;
            }
        }

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

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

            static void Main(string[] args)
            {
                Console.Title = "WCF服务器端";
                using (ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://127.0.0.1:1211/sv")))
                {
                    // 绑定
                    BasicHttpBinding binding = new BasicHttpBinding();
                    binding.Security.Mode = BasicHttpSecurityMode.None;//不需要安全模式
                    host.AddServiceEndpoint(typeof(IService), binding, "/ep");
                    // 服务元数据
                    ServiceMetadataBehavior mb = new ServiceMetadataBehavior();
                    mb.HttpGetEnabled = true;
                    mb.HttpGetUrl = new Uri("http://127.0.0.1:8008/meta");
                    host.Description.Behaviors.Add(mb);
                    host.Opened += (sender, arg) =>
                        {
                            Console.WriteLine("服务已启动。");
                        };
                    try
                    {
                        host.Open();//打开服务
                        Console.Read();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
            }

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

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

        public partial class Form1 : Form
        {
            WS.ServiceClient client = null;
            public Form1()
            {
                InitializeComponent();
                client = new WS.ServiceClient();
                // 关闭通道
                this.FormClosing += (frmsender, frmargs) => client.Close();
            }
    
            private void btnCall1_Click(object sender, EventArgs e)
            {
                int v;
                if (!int.TryParse(this.txtInput.Text, out v))
                {
                    return;
                }
                client.SetValue(v);
            }
    
            private void btnCall2_Click(object sender, EventArgs e)
            {
                int v = client.GetValue();
                this.txtOutput.Text = v.ToString();
            }
        }

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

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

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

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

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

            public void SetValue(int n)
            {
                this.m_Value = n;
                Console.WriteLine("------------------------");
                Console.WriteLine("当前实例的哈希值:{0}", this.GetHashCode().ToString("x"));
                Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);
            }
    
            public int GetValue()
            {
                Console.WriteLine("------------------------");
                Console.WriteLine("当前实例的哈希值:{0}", this.GetHashCode().ToString("x"));
                Console.WriteLine("会话ID:{0}", OperationContext.Current.SessionId);
                return this.m_Value;
            }
     

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

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

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

        [ServiceContract(SessionMode = SessionMode.Required)]
        public interface IService
        {
            。。。。。
         }

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

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

                    // 绑定
                    //BasicHttpBinding binding = new BasicHttpBinding();
                    //binding.Security.Mode = BasicHttpSecurityMode.None;//不需要安全模式
                    //host.AddServiceEndpoint(typeof(IService), binding, "/ep");
                    NetTcpBinding binding = new NetTcpBinding();
                    binding.Security.Mode = SecurityMode.None;
                    host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://127.0.0.1:2377/ep");

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


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

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

     

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

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

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

        [ServiceContract(SessionMode = SessionMode.Required)]
        public interface IService
        {
            [OperationContract(IsOneWay = true,IsInitiating = true, IsTerminating = false)]
            void SetValue(int n);
            [OperationContract]
            int GetValue();
            [OperationContract(IsInitiating = false, IsTerminating = true)]
            void EndSession();
        }

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

            public void EndSession()
            {
                Console.WriteLine("会话结束。");
            }

    看到变化了吗?

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

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

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

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

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

            WS.ServiceClient client = null;
            public Form1()
            {
                InitializeComponent();
                client = new WS.ServiceClient();
                // 关闭通道
                //this.FormClosing += (frmsender, frmargs) => client.Close();
            }
     

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



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

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

  • 相关阅读:
    svn使用教程
    软件工程课程设计分组与选题名单
    解决自己的提问
    <构建之法>第十三章到十七章有感以及这个项目读后感
    <构建之法>第十一章、十二章有感
    关于C语言打印string类字符串的问题
    单链表
    8、判断三角形ABC中是否有点D
    7、完整版的strcpy函数
    6、旋转数组的最小数字
  • 原文地址:https://www.cnblogs.com/yxlblogs/p/3785627.html
Copyright © 2011-2022 走看看