程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 《WCF技術內幕》34:第2部分_第6章_通道:通道接口和基本類型

《WCF技術內幕》34:第2部分_第6章_通道:通道接口和基本類型

編輯:關於.NET

通道接口和基本類型

本章開始部分曾經提到過,學習WCF通道基礎結構的一個關鍵部分就是了解 WCF系統在通道層使用的接口和類型。本節系統整理了這些復雜的類型系統,深 入淺出地講述各個接口和類型的原理,使得讀者可以更容易掌握這些知識點。

IChannel接口

System.ServiceModel.Channels.IChannel接口看似簡單,但是它對於通道層 的實現至關重要。所有的通道和通道工廠必須實現它。換句話說,一個集成了 CommunicationObject的類型通常也會實現IChannel接口。在詳細學習IChannel 接口的作用以前,我們先來看看它的基本結構:

public interface IChannel : ICommunicationObject {
       T GetProperty<T>() where T: class;
}

你或許會問自己:“為什麼會這麼重要呢?”記得CommunicationObject堆棧 裡的每個CommunicationObject 對象都有一些特定的功能,並且只有棧頂的通道 才可以被調用者調用。當堆棧組合正常的情況啊,GetProperty<T>方法提 供了在 CommunicationObject堆棧裡查詢特定功能的途徑。例如,你也許想知道 CommunicationObject堆棧是否支持特定的通道外形,MessageVersion或安全功 能。下面代碼演示了調用如何使用IChannel.GetProperty<T>方法:

// assume channel stack (myChannelStack) created假定通道 堆棧已經創建完畢
MessageVersion messageVersion =
  myChannelStack.GetProperty<MessageVersion>();
if(MessageVersion != null){
  // do something
}
// app continues

和CommunicationObject堆棧裡的其它成員一樣,當一個通道不知道如何響應 查詢的時候,它會使用委托去調用堆棧裡的下一個通道。GetProperty<T> 的簡單實現如下:

public override T GetProperty<T>() where T: class  {
       if (typeof(T) == typeof(MessageVersion)) {
             // this type knows only how to  return MessageVersion
             return (T) this.MessageVersion;
      }
       // no other capabalities are known here, so
       // delegate the query to the next node
       return this.inner.GetProperty<T>();
}

如上所示,這個GetProperty<T>方法的實現可以只返回 MessageVersion,並且它的可以調用查詢堆棧裡的下一個通道的功能。如果查詢 的功能不存在,就會返回null,而不是拋出異常。因為使用了委托來嵌套查詢, 所以只有最底層的通道查詢方法才會拋出null。

數據報通道:IInputChannel與IOutputChannel

第三張裡曾經提到,數據報消息交換模式非常強大而且極具可伸縮性。在數 據報消息交換模式裡,發送者發送一個消息到接收者,而不期望得到回復。更簡 單地說,發送者輸出(發送)一個消息,接收者接受一個消息作為輸入。因此, WCF基礎結構定義了數據報交換模式裡的發送者接口名為 System.ServiceModel.Channels.IOutputChannel,而接受者的接口名為 System.ServiceModel.IInputChannel。

發送接口:IOutputChannel

Like its role in the Datagram MEP, the IOutputChannel interface is simple, as shown here:

和其在數據報交換模式裡角色一樣,IOutputChannel接口比較簡單,如下所 示:

public interface IOutputChannel : IChannel,  ICommunicationObject {
       IAsyncResult BeginSend(Message message,  AsyncCallback callback,
                              Object  state);
       IAsyncResult BeginSend(Message message, TimeSpan  timeout,
                              AsyncCallback callback, Object state);
       void EndSend(IAsyncResult result);
       void Send(Message message);
       void Send(Message message, TimeSpan  timeout);

       EndpointAddress RemoteAddress { get; }
       Uri Via { get; }
}

首先,IOutputChannel實現了IChannel和ICommunicationObject接口。任何 實現了IOutputChannel接口的類型,都必須定義公有的通道狀態機成員和 GetProperty<T>方法。為了支持異步編程模型(APM),接口定義了同步 和異步的Send方法。

RemoteAddress屬性指的是消息發送的地址。值得注意的是,這不一定是消息 發送的真實地址。回憶一下第2章“面向服務”裡的郵政服務的例子,這在一個 消息接收者的情況下,對於標記地址十分有用。IOutputChannel上Via屬性表示 的另外一個地址是消息發送的目標地址。

接收接口:IInputChannel

接收數據報消息的通道實現了IInputChannel接口。對於接收者在數據報交換 模式裡的角色,IInputChannel只定義了接收成員而沒有發送成員。 IInputChannel接口的定義如下:

public interface IInputChannel : IChannel,  ICommunicationObject {
  EndpointAddress LocalAddress { get; }

  // Receive Methods
  IAsyncResult BeginReceive(AsyncCallback callback, Object  state);
  IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback  callback,
                             Object  state);
  Message EndReceive(IAsyncResult result);
  Message Receive();
  Message Receive(TimeSpan timeout);

  // TryReceive Methods
  IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback  callback,
                                Object state);
  bool EndTryReceive(IAsyncResult result, out Message  message);
  bool TryReceive(TimeSpan timeout, out Message  message);

  // Waiting Methods
  IAsyncResult BeginWaitForMessage(TimeSpan timeout,
                                     AsyncCallback callback,
                                     Object state);
  bool EndWaitForMessage(IAsyncResult result);
  bool WaitForMessage(TimeSpan timeout);
}

通常,接收程序會消極地等待消息到來。為此,IInputChannel定義了三個等 待消息的方法。這些方法的命名沒有什麼規律,但是為了簡便,這裡就分為 Receive、TryReceive和WaitForMessage幾組方法。所有的方法都包含同步和異 步定義。

Receive方法等待一段時間,如果消息在這段時間內到達,該方法會返回一個 Message 引用。如果在規定的時間內,消息還沒到達,這些方法就會拋出一個 TimeoutException。TryReceive方法會等待一段時間,然後通過 out參數返回一 個Message引用。這些方法返回一個Boolean值表示能否在期望的時間內返回 Message。Receive和TryReceive方法最大的不同就是如何顯示超時結果。

與Receive和TryReceive 不同,WaitForMessage方法不會返回一個Message引 用,或者一個out參數。它會返回一個表示一個消息是否到達的Boolean值。這有 點像I/O基礎結構裡的Peek功能。把WaitForMessage與Receive或TryReceive一起 使用,可以實現等待一個消息並接受一個消息。

當消息要參與到一些其他的活動中的時候,WaitForMessage方法就非常有用 。比如,思考以下情況,當一個Message必須參與到一個事務裡。在這個例子裡 ,對於Receive和TryReceive方法的調用必須包裝到事物裡。如果Message 沒有 到達,調用者必須終止事務。如果,調用者使用了WaitForMessage方法,這次調 用就沒必要發生在事務的范圍內。如果 WaitForMessage返回false,調用者僅僅 需要再調用WaitForMessage方法。一旦Message到達,調用者能夠啟動一個事務 ,然後調用Receive或TryReceive方法執行相應的任務。

請求/應答通道:IRequestChannel和IReplyChannel

在請求/應答消息交換模式裡,消息的參與者都要發送和接收消息。發送者發 送消息給接收者,然後等待回復。而接收者會接收請求消息,然後發送一個回復 消息。為了實現通道形狀,IRequestChannel和 IReplyChannel接口分別定義了 符合請求/應答消息交換模式的成員.

發送接口:IRequestChannel

IRequestChannel接口定義了發送請求消息和接收應答消息的相關成員。通道 層裡發送和接收消息的成員都包含同步和異步的定義。如下所示:

public interface IRequestChannel : IChannel,  ICommunicationObject {
  // Request Methods
  IAsyncResult BeginRequest(Message message, AsyncCallback  callback,
                             Object  state);
  IAsyncResult BeginRequest(Message message, TimeSpan  timeout,
                             AsyncCallback callback, Object state);
  Message EndRequest(IAsyncResult result);
  Message Request(Message message);
  Message Request(Message message, TimeSpan timeout);

  EndpointAddress RemoteAddress { get; }
  Uri Via { get; }
}

上面的代碼裡,Request方法接受一個Message類型的參數,然後返回一個 Message類型的實例。 這些成員方法的簽名保證了它們符合請求/應答消息交換 模式。

接收接口:IReplyChannel

支持請求/應答消息交換模式的消息接收程序必須實現IReplyChannel接口, IReplyChannel的定義如下:

public interface IReplyChannel : IChannel,  ICommunicationObject {
  RequestContext ReceiveRequest();
  RequestContext ReceiveRequest(TimeSpan timeout);
  IAsyncResult BeginReceiveRequest(AsyncCallback callback,  Object state);
  IAsyncResult BeginReceiveRequest(TimeSpan timeout,
                                     AsyncCallback callback, Object state);
  RequestContext EndReceiveRequest(IAsyncResult result);

  Boolean TryReceiveRequest(TimeSpan timeout, out  RequestContext context);
  IAsyncResult BeginTryReceiveRequest(TimeSpan timeout,
                                        AsyncCallback callback,
                                        Object state);
  Boolean EndTryReceiveRequest(IAsyncResult result,
                                out  RequestContext context);

  Boolean WaitForRequest(TimeSpan timeout);
  IAsyncResult BeginWaitForRequest(TimeSpan timeout,
                                     AsyncCallback callback,
                                     Object state);
  bool EndWaitForRequest(IAsyncResult result);

  EndpointAddress LocalAddress { get; }
}

IReplyChannel裡沒有直接返回一個Message實例的成員。相反, IReplyChannel接口支持通過RequestContext類型訪問接收到的Message實例。下 一節會詳細討論 RequestContext類型。現在,我們該知道接收到的消息對於 RequestContext類型是可見的,並且可以通過 RequestContext訪問消息實例。

像IInputChannel一樣,IReplyChannel 也定義了幾類接收消息的方法。 ReceiveRequest方法返回一個RequestContext實例,並且超時的時候,會拋出異 常。 TryReceiveRequest會返回一個Boolean類型的值來表示是否在規定的時間 內接收到消息。WaitForRequest方法,和 IInputChannel接口上的 WaitForMessage方法類似,返回的結果取決於請求消息或是否超時。

請求/應答關聯:RequestContext類型

在請求/應答消息交換模式裡,請求和應答是緊密關聯的。從發送者的角度來 看,請求通常會返回一個應答消息。從接受者的角度來看,一個接收到的消息必 須產生一個應答消息。如前所述,IReplyChannel使用RequestContext作為 ReceiveRequest方法的返回類型。這是請求/應答消息交換模式下,接收通道關 聯消息的首要方式。

更高層次上,RequestContext類型包裝了請求消息,而且提供了發送應答消 息給發送者的方法。在RequestContext裡,可以通過RequestMessage屬性查看請 求消息。RequestContext的Reply方法提供了發送應答消息的途徑。和其它的通 道成員一樣,reply方法對於同步和異步方法都是可見的。下面代碼展示了 RequestContext類型的成員:

public abstract class RequestContext : IDisposable  {
  protected RequestContext();

  public abstract void Abort();

  public abstract void Reply(Message message);
  public abstract void Reply(Message message, TimeSpan  timeout);
  public abstract IAsyncResult BeginReply(Message message,
                                            AsyncCallback callback,
                                            Object state);
  public abstract IAsyncResult BeginReply(Message message,
                                            TimeSpan timeout,
                                            AsyncCallback callback,
                                            Object state);
  public abstract void EndReply(IAsyncResult result);

  public abstract void Close();
  public abstract void Close(TimeSpan timeout);

  protected virtual void Dispose(Boolean disposing);
  void IDisposable.Dispose();

  public abstract Message RequestMessage { get; }
}

如代碼所示, RequestContext實現了IDisposable 接口。因為通道層裡其它 成員沒有實現IDisposable接口,所以這裡就很難看出為什麼RequestContext類 型會這麼干。 RequestContext類型實現IDisposable接口是因為RequestContext 包含了一個Message實例。第4章裡 “WCF101”裡曾經討論過,Message實例包含 一個Stream,因此必須實現IDisposable接口。因為這種關系,RequestContext 類型上的Dispose方法需要調用Message實例上的Dispose方法,這樣才能銷毀 Message 實例擁有的Stream。記住RequestContext是一個抽象類型,因此繼承 RequestContext的類型可以根據需要提供自己的實現。

注釋:與Message類型一樣,RequestContext類型明確地實現了IDisposable 接口

雙工通道:IDuplexChannel

雙工通道支持雙工消息交換模式(MEP)。與數據報和請求/應答消息交換模 式不同的是,雙工消息交換模式運行發送者和接收者自由地發送和接受消息。我 們在第3章裡曾經看到,雙工消息交換模式裡的消息通信很像電話通話。在開始 通信以前,發送者和接收者必須建立通信上下文環境。在,雙工消息交換模式裡 ,發送和接受通道形狀是相同的,因此,發送者和接收者實現了相同的接口(假 設連個消息參與者都是WCF程序)。因為雙工消息交換模式的與生俱來的自由特 性,以及接收者和發送公用相同的接口,因此只能通過發起通信來區分哪個是消 息的發送者(就像只能通過誰先撥號,來確定誰是打電話的人一樣)。

發送和接收接口:IDuplexChannel

IDuplexChannel接口實際上是IInputChannel和IOutputChannel的結合體。如 前文所述,IInputChannel 是為了實現了數據報消息接收者,而IOutputChannel 是為了實現數據報發送者。因為支持雙工通信的通道必須能夠發送和接受消息, 所以邏輯上,IDuplexChannel成員是數據報交換模式裡使用的所有成員合並的結 果。IDuplexChannel接口的定義如下:

public interface IDuplexChannel : IInputChannel,  IOutputChannel, IChannel,
  ICommunicationObject
{
}

IDefaultCommunicationTimeouts接口

因為大部分應用程序開發人員都不回直接接觸通道,因此通道層必須有一種 表示特定操作超時的方法。考慮到通道超時問題的時候,有4個時間敏感的操作 :打開通道、發送消息、接受消息和關閉通道。和通道層的其它功能一樣,WCF 類型系統包含一個描述超時的接口。System.ServiceModel. IDefaultCommunicationTimeouts,定義如下:

public interface IDefaultCommunicationTimeouts {
       TimeSpan CloseTimeout { get; }
       TimeSpan OpenTimeout { get; }
       TimeSpan ReceiveTimeout { get; }
       TimeSpan SendTimeout { get; }
}

IDefaultCommunicationTimeouts接口裡每個成員的作用可以從你名字裡推測 出來。綁定、通道工廠和通道都實現了這個接口。因為綁定、通道工廠和通道實 現了相同的接口,這些類型都可以傳遞超時到構造鏈中。例如,一個用戶可以在 Binding裡指定發送超時屬性(Binding提供了setter器)。如果Binding是消息 發送者的一部分,它就可以把超時屬性的值通過通道工廠的構造函數傳遞給通道 工廠。同樣,通道工廠也可以把超時屬性的值傳遞給通道的構造函數。作用上看 ,這一系列的傳遞提供給用戶可以通過API指定超時屬性的能力,並且這些設置 可以作用於通道層上。

ChannelBase類型

所有的自定義通道必須實現公共的狀態機,並且暴露GetProperty<T> 查詢機制,實現一個或者多個通道形狀,從通道工廠裡接受一個超時設置。 System.ServiceModel.Channels.ChannelBase抽象類型就是這個目的,它確保了 所有的通道成員的兼容性。下面代碼展示了ChannelBase的類型定義:

public abstract class ChannelBase :  CommunicationObject,
                                      IChannel,
                                      ICommunicationObject,
                                      IDefaultCommunicationTimeouts {
  // Constructor with channel factory parameter
  protected ChannelBase(ChannelManagerBase channelManager);
  // IChannel implementation
  public virtual T GetProperty<T>() where T: class;
  // CommunicationObject members
  protected override TimeSpan DefaultCloseTimeout { get; }
  protected override TimeSpan DefaultOpenTimeout { get; }
  protected override void OnClosed();
  protected TimeSpan DefaultReceiveTimeout { get; }
  protected TimeSpan DefaultSendTimeout { get; }
  // IDefaultCommunicationTimeouts implementation
  TimeSpan IDefaultCommunicationTimeouts.CloseTimeout { get; }
  TimeSpan IDefaultCommunicationTimeouts.OpenTimeout { get; }
  TimeSpan IDefaultCommunicationTimeouts.ReceiveTimeout { get;  }
  TimeSpan IDefaultCommunicationTimeouts.SendTimeout { get; }
  // reference to channel factory
  protected ChannelManagerBase Manager { get; }
  private ChannelManagerBase channelManager;
}

ChannelManagerBase的成員表示的是工廠創建通道的方法。第7 章“通道管 理器”裡會詳細介紹這些內容。形狀,假設ChannelManagerBase類型一直會從通 道工廠裡獲取超時設置的值。注意 ChannelBase裡的TimeSpan類型的成員。以 Default開頭的成員都會從通道工廠裡獲取超時設置的值,而且這裡明確實現了 IDefaultCommunicationTimeouts的成員。如下所示:

protected override TimeSpan DefaultOpenTimeout {
  get {
    return ((IDefaultCommunicationTimeouts) this.channelManager).OpenTimeout;
  }
}
// delegate to DefaultOpenTimeout property TimeSpan
IDefaultCommunicationTimeouts.OpenTimeout {
  get {
     return this.DefaultOpenTimeout;
  }
}

上面的代碼僅僅介紹了通道裡open如何實現超時值的傳遞。close、send和 receive方法實現的方式類似。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved