閱讀50 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[WCF的Binding模型]之三:信道監聽器(Channel Listener)

信道管理器是信道的創建者,一般來說信道棧的中每個信道對應著一個信道管理器。基於不同的消息處理的功能,將我們需要將相應的信道按照一定的順序能組織起來構成一個信道棧,由於信道本身是由信道管理器創建的,所以信道對應的信道管理器也構成一個信道管理器棧,棧中信道管理器的順序決定由它所創建信道的順序。

對於WCF的信道層來說,信道管理器在服務端和客戶端扮演著不同的角色,服務端的信道管理器在於監聽來自客戶端的請求,而客戶端的信道僅僅是單純的創建用於消息發送的信道。因此,客戶端的消息管理器又稱為信道監聽器(Channel Listener),客戶端的信道管理器則成為信道工廠(channel factory)。

在WCF中,所有的信道管理器,不管是位於服務端的信道監聽器還是客戶端的信道工廠,都繼承自一個基類:System.ServiceModel.Channels.ChannelManagerBase。ChannelManagerBase直接繼承自CommunicationObject,並實現了接口IDefaultCommunicationTimeouts。

   1: public abstract class ChannelManagerBase : CommunicationObject, IDefaultCommunicationTimeouts 
   2: { 
   3:    // ...... 
   4: }

其實我們完全可以把一個WCF應用開成是一個普通的基於監聽-請求模式的網絡應用,服務端將監聽器綁定到一個或一組URI上進行網絡監聽,一旦成功監聽到來自客戶端的請求,則接收、處理該請求,如需回複則發送回複回客戶端。在整個過程中,監聽器處於核心的地位,而WCF中的信道監聽器就起著這樣的作用。

熟悉網絡編程的朋友一定會對套節字應用編程接口(Berkeley Sockets API)不會陌生,通過Socket API,我們很容易的創建基於網絡監聽-請求的應用程序。在.NET編程環境下,我們將System.Net.Sockets.TcpListener 或者System.Net.Sockets.Socket 對象綁定到一個URI上,讓他們監聽來自客戶端的連接。當連接請求被成功監測到,調用Accept相關方法或者方法創建一Socket或者TcpClient對象,並通過這些對象獲得請求消息。

WCF中的信道監聽器與之相似。當我們對一個服務進行寄宿的時候,會為之添加一個或者多個終結點。對於一個終結點來說,它具有一個代表邏輯地址的終結點地址,還有一個代表物理地址的監聽地址(關於邏輯地址和物理地址,請參閱第二章),如果監聽地址(ListenUri)沒有顯式地指定,則監聽地址和邏輯地址共享相同的URI。對於每一個不同監聽地址,WCF會通過具體的綁定對象創建一個信道監聽器。信道監聽器通過調用AcceptChannel創建監聽信道棧,位於信道棧的第一個信道被成功返回。

一旦消息請求被成功監聽,如果該信道是InputChannel(數據報MEP) 或者DuplexChannel(雙工MEP),則調用Receive或者BeginReceive方法接收消息,如果需要向對象發送消息,則通過Send或者BeginSend將消息發給請求者;如果信道是ReplyChannel(請求/回複MEP)則調用ReceiveRequest方法獲得一個RequestContext對象,通過該對象獲取請求消息並發送回複消息。

由於信道監聽器是位於服務端的信道管理器,所以所有的信道監聽器均繼承自基類:ChannelManagerBase。同時由於信道監聽器具有其特殊的請求監聽的功能,所以WCF還定義一些相關的接口,比如System.ServiceModel.Channels.IChannelListener和System.ServiceModel.Channels.IChannelListener<TChannel>。

IChannelListener繼承自ICommnucationObject接口。定義了一組WaitForChannel和BeginWaitForChannel/EndWaitForChannel以同步和異步的方式判斷是否具有一個可用的信道;GetProperty<T>和IChannel的GetProperty<T>相對;Uri屬性返回真正的監聽地址。

   1: public interface IChannelListener : ICommunicationObject
   2: {
   3:     IAsyncResult BeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state);
   4:     bool EndWaitForChannel(IAsyncResult result);
   5:     T GetProperty<T>() where T : class;
   6:     bool WaitForChannel(TimeSpan timeout);
   7:  
   8:     Uri Uri { get; }
   9: }

範型類型的IChannelListener<TChannel>繼承自IChannelListener,範型類型TChannel是一個實現了IChannel的類,一般來說,TChannel代表基於某種channel shape的Channel, 比如實現了IOutputChannel、IInputChannel、IRequestChanne、IReplyChannel、IDuplexChannel的IChannel類型。定義在IChannelListener<TChannel>的AcceptChannel和BeginAcceptChannel/EndAcceptChannel在連接請求被監聽到時,以同步或者異步的方式創建信道棧用於消息的接收。

   1: public interface IChannelListener<TChannel> : IChannelListener, ICommunicationObject where TChannel : class, IChannel
   2: {
   3:     // Methods
   4:     TChannel AcceptChannel();
   5:     TChannel AcceptChannel(TimeSpan timeout);
   6:     IAsyncResult BeginAcceptChannel(AsyncCallback callback, object state);
   7:     IAsyncResult BeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state);
   8:     TChannel EndAcceptChannel(IAsyncResult result);
   9: }  

除了定義兩個接口外,WCF中還定義了與這兩個接口向對應的抽象基類:System.ServiceModel.Channels.ChannelListenerBase和System.ServiceModel.Channels.ChannelListenerBase<TChannel>。ChannelListenerBase實現了接口IChannelListener,而ChannelListenerBase<TChannel>實現了接口IChannelListener<TChannel>。

   1: public abstract class ChannelListenerBase : ChannelManagerBase, IChannelListener, ICommunicationObject
   2: { 
   3:     //... ...
   4: }
   5: public abstract class ChannelListenerBase<TChannel> : ChannelListenerBase, IChannelListener<TChannel>, IChannelListener, ICommunicationObject where TChannel : class, IChannel
   6: {
   7:     //... ...
   8: }

圖1所示的類圖大體上表示了上述的這些基類和接口之間的關係:

image

圖1 信道監聽器接口與基類

在上麵一節的案例演示中,我們創建了兩個用於請求-回複消息交換模式下的自定義信道,一個是實現了IRequestChannel的SimpleRequestChannel.,另一個是實現了IReplyChannel的SimpleReplyChannel。在本案例以及接下來的案例演示中,我們將為這兩個自定義創建兩個相應的信道管理器,其實一個是用於創建SimpleRequestChannel的自定義信道工廠,另一個則是創建SimpleReplyChannel的自定義信道監聽器。先來看看我們自定義的信道監聽器SimpleChannelListener<TChannel>。該類繼承自範型的ChannelListenerBase<TChannel>:

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:     //... ...
   4: }

我們說過信道一般不會孤立地存在,而是存在於一個由多個信道按照一定順序構成的信道棧中。由於信道管理器是信道的締造者,要創建整個信道棧,同樣需要這些信道對應的信道管理器按照相應的順序組成一個信道管理器棧。反映在具體實現上,當執行了某個方法之後,需要調用棧中後一個信道監聽器相應的方法,所以在SimpleChannelListener<TChannel>中,定義一個字段_innerChanneListener,代表棧中與之相鄰的信道監聽器。_innerChanneListener通過在構造函數中指定的BindingContext對象創建。關於BindingContext,我將在後麵的一節中左詳細的介紹。

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:     ... ...
   4:     private IChannelListener<TChannel> _innerChanneListener; 
   5:  
   6:     public SimpleChannelListener(BindingContext context)
   7:     {
   8:         PrintHelper.Print(this, "SimpleChannelListener");
   9:         this._innerChanneListener = context.BuildInnerChannelListener<TChannel>();
  10:     }
  11: }

對於SimpleChannelListener<TChannel>來說,它的最重要的功能就是創建我們自定義的ReplyChannel:SimpleReplyChannel。SimpleReplyChannel的創建實現在OnAcceptChannel和OnEndAcceptChannel方法中。在構造SimpleReplyChannel的innerChannel通過_innerChanneListener的AcceptChannel方法創建。

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:     ... ... 
   4:  
   5:     protected override TChannel OnAcceptChannel(TimeSpan timeout)
   6:     {
   7:         PrintHelper.Print(this, "OnAcceptChannel");
   8:         IReplyChannel innerChannel = this._innerChanneListener.AcceptChannel(timeout) as IReplyChannel;
   9:         return new SimpleReplyChannel(this, innerChannel) as TChannel;
  10:     }
  11:     protected override IAsyncResult OnBeginAcceptChannel(TimeSpan timeout, AsyncCallback callback, object state)
  12:     {
  13:         PrintHelper.Print(this, "OnBeginAcceptChannel");
  14:         return this._innerChanneListener.BeginAcceptChannel(timeout, callback, state); 
  15:  
  16:     } 
  17:  
  18:     protected override TChannel OnEndAcceptChannel(IAsyncResult result)
  19:     {
  20:         PrintHelper.Print(this, "OnEndAcceptChannel");
  21:         return new  SimpleReplyChannel(this,this._innerChanneListener.EndAcceptChannel(result) as IReplyChannel) as TChannel;
  22:     }
  23: }

對於定義在基類必須實現的抽象方法來說,為了簡單起見,我們僅僅是通過PrintHelper輸出當前執行的方法名稱,然後調用_innerChanneListener的相應的方法就可以了: 

   1: public class SimpleChannelListener<TChannel> : ChannelListenerBase<TChannel> where TChannel : class, IChannel
   2: {
   3:       protected override IAsyncResult OnBeginWaitForChannel(TimeSpan timeout, AsyncCallback callback, object state)
   4:         {
   5:             PrintHelper.Print(this, "OnBeginWaitForChannel");
   6:             return this._innerChanneListener.BeginWaitForChannel(timeout, callback, state); 
   7:         } 
   8:         protected override bool OnEndWaitForChannel(IAsyncResult result)
   9:         {
  10:             PrintHelper.Print(this, "OnEndWaitForChannel");
  11:             return this._innerChanneListener.EndWaitForChannel(result);
  12:         } 
  13:         protected override bool OnWaitForChannel(TimeSpan timeout)
  14:         {
  15:             PrintHelper.Print(this, "OnWaitForChannel");
  16:             return this._innerChanneListener.WaitForChannel(timeout);
  17:         }
  18:     //... ...
  19: }
  20:  
  21:  

WCF中的綁定模型:
[WCF中的Binding模型]之一: Binding模型簡介
[WCF中的Binding模型]之二: 信道與信道棧(Channel and Channel Stack)
[WCF中的Binding模型]之三:信道監聽器(Channel Listener)
[WCF中的Binding模型]之四:信道工廠(Channel Factory)
[WCF中的Binding模型]之五:綁定元素(Binding Element)
[WCF中的Binding模型]之六:從綁定元素認識係統預定義綁定



作者:蔣金楠 
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-30 16:04:13

  上一篇:go  Enterprise Library深入解析與靈活應用(5):創建一個簡易版的批處理執行器,認識Enterprise Library典型的配置方式和對象創建方式
  下一篇:go  Enterprise Library深入解析與靈活應用(6):自己動手創建迷你版AOP框架