WCF技术剖析(卷1)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.4.1 信道监听器(Channel Listener)

我们完全可以把一个WCF应用看成是一个普通的基于监听-请求模式的网络应用,服务端将监听器绑定到一个或一组URI上进行网络监听,一旦成功监听到来自客户端的请求,则接收、处理该请求,如需回复则发送回复给客户端。在整个过程中,监听器处于核心的地位,而WCF中的信道监听器就起着这样的作用。

关于信道监听器的监听过程

熟悉网络编程的朋友对套节字应用编程接口(Berkeley Sockets API)一定不会陌生,通过Socket API,我们很容易创建基于网络监听-请求的应用程序。在.NET编程环境下,将System.Net.Sockets.TcpListener或System.Net.Sockets.Socket对象绑定到一个URI上,让他们监听来自客户端的连接。当连接请求被成功监测到,调用Accept相关方法创建Socket或TcpClient对象,并通过这些对象获得请求消息。WCF中的信道监听器与之相似,但对于数据报信道(Datagram Channel)和会话信道(Sessionful Channel),其监听机制具有一些差别。

1.基于数据报信道(Datagram Channel)的监听机制

当我们对一个服务进行寄宿的时候,会为之添加一个或多个终结点。对于一个终结点来说,它具有一个代表逻辑地址的终结点地址,还有一个代表物理地址的监听地址(关于逻辑地址和物理地址,请参阅第2章),如果监听地址(ListenUri)没有显式地指定,则监听地址和逻辑地址共享相同的URI。对于每一个不同监听地址,WCF会通过具体的绑定对象创建一个信道监听器。开始监听的同时,监听信道栈会被创建出来。

一旦消息请求被成功监听,无论来自哪个客户端(服务代理),信道监听器都会使用创建好的信道栈对请求消息进行接收、处理。对于服务端来说,相同的监听地址采用同一个信道栈。

2.基于会话信道(Sessionful Channel)的监听机制

对于采用会话的场景,WCF的监听机制有所不同。由信道监听器创建的信道栈不是共享的,而是和具体的客户端一一匹配的,也就是说,不同的客户端独占地使用一个服务端信道栈。正因为如此,信道栈的创建时机选择在成功监听到消息请求之时,而不是监听的开始。

对于来自某个客户端的第一次请求,信道监听器会为之创建一个新的信道栈,并利用它来接收、处理该请求。创建的信道栈会被直接用于同一个客户端的后续请求的接收。由于WCF会为每一个活动的(Active)客户端维护一个会话信道栈,为了避免并发的信道栈过多导致服务端资源的过度占用,WCF具有最大并发会话数量的限制。一旦超过这个限制,信道将不会被创建,相应的请求也就得不到及时的处理。

也正是这个原因,对于基于会话信道的服务调用,应该及时地关闭。当客户端显式地关闭会话信道栈时,服务会接收到相应的请求,从而将服务端的会话信道关闭。

信道监听器相关编程接口

由于信道监听器是位于服务端的信道管理器,所以所有的信道监听器均继承自基类:ChannelManagerBase。WCF还定义一些相关的接口来抽象信道监听器的基本行为。典型的接口包括System.ServiceModel.Channels.IChannelListener和System.ServiceModel.Channels. IChannelListener<TChannel>。

IChannelListener继承自ICommunicationObject接口。定义了一组WaitForChannel和BeginWaitForChannel/EndWaitForChannel方法,以同步和异步的方式等待信道的抵达;GetProperty<T>和IChannel的GetProperty<T>相对;URI属性返回真正的监听地址。

        public interface IChannelListener : ICommunicationObject
        {
            IAsyncResult BeginWaitForChannel(TimeSpan timeout,AsyncCallback callback,
              object state);
            bool EndWaitForChannel(IAsyncResult result);
            T GetProperty<T>() where T : class;
            bool WaitForChannel(TimeSpan timeout);
            Uri Uri { get; }
        }

IChannelListener<TChannel>继承自IChannelListener,TChannel是一个实现了IChannel接口的信道。一般来说,TChannel代表基于某种信道形状的信道, 比如实现了IOutputChannel、IInputChannel、IRequestChannel、IReplyChannel、IDuplexChannel的信道。定义在IChannelListener<TChannel>的AcceptChannel和BeginAcceptChannel/EndAcceptChannel以同步或异步的方式创建用于接收处理消息的信道栈。

        public interface IChannelListener<TChannel> : IChannelListener,
          ICommunicationObject where TChannel : class, IChannel
        {
            TChannel AcceptChannel();
            TChannel AcceptChannel(TimeSpan timeout);
            IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state);
            IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback,
              object state);
            TChannel EndAcceptChannel(IAsyncResult result);
        }

除了定义这两个接口,WCF中还定义了与这两个接口相对应的抽象基类:System.ServiceModel.Channels.ChannelListenerBase和System.ServiceModel.Channels.Channel-ListenerBase<TChannel>,它们分别实现了上面的两个接口。图3-13所示的类图大体上表示了上述这些基类和接口之间的关系:

图3-13 信道监听器接口与基类

        public abstract class ChannelListenerBase : ChannelManagerBase,
          IChannelListener, ICommunicationObject
        {
            //省略成员
        }
        public abstract class ChannelListenerBase<TChannel> : ChannelListenerBase,
          IChannelListener<TChannel>, IChannelListener, ICommunicationObject where
          TChannel : class, IChannel
        {
            //省略成员
        }

案例演示:如何自定义信道监听器

在上一节的案例演示中创建了两个服务端信道:数据报信道SimpleReplyChannel和会话信道SimpleDuplexSessionChannel。接下来,分别为它们创建相应的信道监听器。

1.创建数据报信道监听器

先来看看自定义的信道监听器SimpleDatagramChannelListener<TChannel>,该类继承自泛型的ChannelListenerBase<TChannel>:

        public class SimpleDatagramChannelListener<TChannel> :
          ChannelListenerBase<TChannel> where TChannel : class, IChannel
        {
            //省略成员
        }

某一个信道监听器只负责某个信道的创建,整个信道栈的创建依赖于相关信道监听器的协同工作。与创建自定义信道类似,在创建自定信道监听器时,我们会为之指定一个内部信道监听器(_innerChannelListener),它代表信道栈中下一个信道对应的信道监听器。在构造函数中,_innerChannelListener通过传入的BindingContext创建。

        public class SimpleDatagramChannelListener<TChannel> :
          ChannelListenerBase<TChannel> where TChannel : class, IChannel
        {
            //其他成员
            private IChannelListener<TChannel> _innerChanneListener;
            public SimpleDatagramChannelListener(BindingContext context)
            {
                PrintHelper.Print(this, "SimpleDatagramChannelListener");
                this._innerChanneListener = context.BuildInnerChannelListener
                  <TChannel>();
            }
        }

对于SimpleDatagramChannelListener<TChannel>来说,它最重要的功能就是创建我们自定义的ReplyChannel:SimpleReplyChannel。SimpleReplyChannel的创建实现在OnAcceptChannel和OnEndAcceptChannel方法中。

        public class SimpleDatagramChannelListener<TChannel> :
          ChannelListenerBase<TChannel> where TChannel : class, IChannel
        {
            //其他成员
            protected override TChannel OnAcceptChannel(TimeSpan timeout)
            {
                PrintHelper.Print(this, "OnAcceptChannel");
                IReplyChannel innerChannel = this._innerChanneListener.AcceptChannel
                  (timeout) as IReplyChannel;
                return new SimpleReplyChannel(this, innerChannel) as TChannel;
            }
            protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout,
              AsyncCallback callback, object state)
            {
                PrintHelper.Print(this, "OnBeginAcceptChannel");
                return this._innerChanneListener.BeginAcceptChannel(timeout,
                  callback, state);
            }
            protected override TChannel OnEndAcceptChannel(IAsyncResult result)
            {
                PrintHelper.Print(this, "OnEndAcceptChannel");
                return new SimpleReplyChannel(this,this._innerChanneListener.
                  EndAcceptChannel(result) as IReplyChannel) as TChannel;
            }
        }

对于从基类继承下来的抽象方法和属性,在通过PrintHelper输出相应信息之后,调用后续监听器(_innerChannelListener)的相应方法即可。

        public class SimpleDatagramChannelListener<TChannel> :
          ChannelListenerBase<TChannel> where TChannel : class, IChannel
        {
              //其他成员
              protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout,
                AsyncCallback callback, object state)
                {
                    PrintHelper.Print(this, "OnBeginWaitForChannel");
                    return this._innerChanneListener.BeginWaitForChannel(timeout,
                      callback, state);
                }
                protected override bool OnEndWaitForChannel(IAsyncResult result)
                {
                    PrintHelper.Print(this, "OnEndWaitForChannel");
                    return this._innerChanneListener.EndWaitForChannel(result);
                }
                protected override bool OnWaitForChannel(TimeSpan timeout)
                {
                    PrintHelper.Print(this, "OnWaitForChannel");
                    return this._innerChanneListener.WaitForChannel(timeout);
                }
        }

2.创建会话信道监听器

接下来,我们为自定义会话信道SimpleDuplexSessionChannel创建信道监听器SimpleSessionChannelListener<TChannel>。定义完全与上面的类似,唯一须要特别说明的是,须要重写GetProperty<T>方法。

        using System;
        using System.ServiceModel.Channels;
        namespace Artech.CustomChannels
        {
            public class SimpleSessionChannelListener<TChannel>:
              ChannelListenerBase<TChannel> where TChannel:class,IChannel
            {
            //其他成员
                private IChannelListener<TChannel> _innerChanneListener;
                public SimpleSessionChannelListener(BindingContext context)
                {
                    PrintHelper.Print(this, "SimpleSessionChannelListener");
                    this._innerChanneListener = context.BuildInnerChannelListener
                      <TChannel>();
                }
                protected override TChannel OnAcceptChannel(TimeSpan timeout)
                {
                    PrintHelper.Print(this, "OnAcceptChannel");
                    IDuplexSessionChannel innerChannel = this._innerChanneListener.
                      AcceptChannel(timeout) as IDuplexSessionChannel;
                    return new SimpleDuplexSessionChannel(this, innerChannel) as
                      TChannel;
                }
                public override T GetProperty<T>()
                {
                    return this._innerChanneListener.GetProperty<T>();
                }
            }
        }