WCF中的Binding模型之六(完結篇):從繫結元素認識系統預定義繫結

行者武松發表於2017-10-30

由於繫結物件由一系列有序的繫結元素組成,繫結元素最終決定著通道棧中通道的組成,而通道的組成最終又決定了通道棧對訊息進行處理的方式和能力,所有要確定繫結的特性和能力,我們可以通過檢視其繫結元素的構成來一窺究竟。為此我們我們寫了一個簡單的方法,用於列出一個具體的繫結物件所有的繫結元素,在介紹一個個具體的系統繫結中,我會使用該方法:

   1: static void ListAllBindingElements(Binding binding)
   2: {
   3:     BindingElementCollection elements = binding.CreateBindingElements();
   4:     for (int i = 0; i < elements.Count; i++)
   5:     {
   6: Console.WriteLine("{0}. {1}", i+1, elements[i].GetType().FullName);
   7:     }
   8: }

 

一、BasicHttpBinding

我們通過呼叫預設的建構函式建立一個繫結物件,並藉助上面的ListAllBindingElements方法列出該繫結物件所有的繫結元素:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         BasicHttpBinding binding = new BasicHttpBinding();
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

 

將會得到如下的輸出,從中可以看出在預設的情況下,一個BasicHttpBinding包含兩個最基本的繫結元素:最為傳輸元素的HttpTransportBindingElement和作為訊息編碼元素的TextMessageEncodingBindingElement。所以BasicHttpBinding在預設的情況下采用HTTP傳輸協議,和基於文字的訊息編碼方式。之所以將此繫結命名為BasicHttpBinding,很大程度上緣於它僅僅包含一些最基本的用於訊息通訊的元素。


除了提供最基本的傳輸和編碼功能外,BasicHttpBinding還提供了對安全的支援,無論是基於傳輸的安全還是基於訊息的安全,都可以通過對繫結進行相應的設定實現。我們同樣通過列出繫結元素的方式來證明這一點。下面的程式碼中,在建立BasicHttpBinding物件的時候,指定一個BasicHttpSecurityMode.Transport引數將安全模式設為傳輸安全模式:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

 

在最終的輸出中我們可以看到,傳輸繫結元素由HttpTransportBindingElement變成了HttpsTransportBindingElement,由此可以看出BasicHttpBinding通過HTTPS實現傳輸安全。


如果我們設定成基於訊息的安全模式,並將客戶端的憑證型別(Client Credential Type)設為證書(Certificate,這對於基於訊息安全模式的BasicHttpBinding是必須的)。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Message);
   6:         binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;
   7:         ListAllBindingElements(binding);
   8:     }
   9: }

那麼通過輸出,我們可以看到在原有繫結元素之上,又多出了一個新的繫結元素:AsymmetricSecurityBindingElement,該元素通過非對稱加密(也就是基於X509證書的加密方式)的方式實現了基於訊息的安全。


對於BasicHttpBinding來說,預設採用基於文字的訊息編碼方式(TextMessageEncodingBindingElement),實際上BasicHttpBinding還提供對基於MTOM編碼方式的支援。我們可以通過程式設計或者配置的方式對訊息編碼方式進行顯式指定。在下面的程式碼中,通過MessageEncoding屬性將編碼方式指定為:WSMessageEncoding.Mtom。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         BasicHttpBinding binding = new BasicHttpBinding();
   6:         binding.MessageEncoding = WSMessageEncoding.Mtom;
   7:         ListAllBindingElements(binding);
   8:     }
   9: }

 

那麼我們我們最終輸出的繫結元素列表中,TextMessageEncodingBindingElement將會被實現MTOM訊息編碼的MtomMessageEncodingBindingElement代替。


BasicHttpBinding是WS-BP 1.1 Spec (Basic Profile) 標準的,ASP.NET ASMX Web
Service的很多標準存在於WS-BP 1.1 Spec中,比如SOAP 1.1、WSDL 1.1、Message Security
1.0等等,所以BasicHttpBinding可以和傳統的ASP.NET ASMX Web Service進行互操作。

二、 WsHttpBinding

我們通過與BasicHttpBinding的方式來分析WsHttpBinding,先通過下面的方式列出在預設條件下(通過預設的建構函式建立WsHttpBinding物件)該繫結物件具有的所有繫結元素:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         WsHttpBinding binding = new WsHttpBinding();
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

 

從下面的輸出來看,從上到下一共包含4個繫結元素:TransactionFlowBindingElement、SymmetricSecurityBindingElement、TextMessageEncodingBindingElement和HttpTransportBindingElement。TransactionFlowBindingElement實現了對事物流轉;SymmetricSecurityBindingElement通過對稱加密的方式實現基於訊息的安全;TextMessageEncodingBindingElement和HttpTransportBindingElement表明WsHttpBinding和BasicHttpBinding一樣採用基於文字的編碼方式和基於HTTP的傳輸協議。


在這了需要特別指出的就是WsHttpBinding對事務的支援。對於SOA來說,事務永遠是一個重要的主題,我們不僅僅需要單方的事務支援,比如將服務端的操作納入一個單一的事務之中,也需要事務的流轉,將從客戶端開始的事務自動流向服務端;不僅僅需要基於單次服務呼叫的事務,還需要基於多次服務訪問的事務(將多次服務呼叫納入同一個事務之中);不僅僅需要基於單一平臺的事務支援,還需要跨平臺的事務(比如將基於.NET平臺的WCF服務呼叫和基於J2EE平臺的Web服務呼叫納入同一個事務中)。在WS-*體系中,WS-AT為事務定義了規範,而在WCF中,則通過TransactionFlowBindingElement實現了WS-AT規範。

WsHttpBinding在預設的情況下就提供了對基於訊息安全的支援,此外WsHttpBinding仍然提供基於HTTPS的傳輸安全。在下面我們對程式碼稍加改動,通過建構函式將WsHttpBinding設定為基於傳輸的安全模式:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         WsHttpBinding binding = new WsHttpBinding(SecurityMode.Transport);
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

那麼基於訊息模式的SymmetricSecurityBindingElement將被去除,而作為傳輸繫結元素的HttpTransportBindingElement將被替換成HttpsTransportBindingElement,藉此實現基於HTTPS的傳輸安全。


除了提供對傳輸和訊息安全的支援之外,WsHttpBinding還對傳輸的可靠性提供支援。可靠性訊息傳輸確保在網路環境不好的情況下訊息的有效、有序抵達。WS-*通過WS-RM(Reliable
Messaging)為可靠傳輸定義了規範,在WCF中WS-RM通過可靠會話(Reliable
Session)實現了WS-RM,而WS-RM在WCF的實現通過ReliableSessionBindingElement承載。下面的程式碼中,我們通過另一個建構函式設定WsHttpBinding對可靠會話的支援(第二個引數代表是否支援可靠會話)。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message, true);
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

最終的輸出將包含五個繫結元素,第二個就是實現了可靠會話的ReliableSessionBindingElement。


 

此外,和BasicHttpBinding一樣,WsHttpBinding定義了型別為System.ServiceModel.WSMessageEncoding列舉型別的MessageEncoding屬性,有兩種WSMessageEncoding列舉值供你選擇:Text和MTOM。

綜上所述,WsHttpBinding對大部分的WS-*提供支援,這包括我們上面提到的WS-Transactions、WS-Security、WS-Reliable
Messaging等等。所以從互操作角度講,WsHttpBinding可以和滿足這些標準的Web Service進行互操作。

三、 WsDualHttpBinding

在前面對訊息交換模式的介紹中,我們談到三種典型的訊息交換模式:單向的資料包模式、請求/回覆模式和雙工模式。WsDualHttpBinding就是專門為HTTP傳輸下雙工訊息交換模式設計的。

除了基於傳輸的安全之外,WsHttpbing的所有的特性都被WsDualHttpBinding繼承下來,這包括:基於HTTP的傳輸、基於文字和MTOM的訊息編碼、WS-Security、WS-Transactions、WS-Reliable
Messaging(Reliable Session)等等。我們仍然通過輸出繫結元素的方式證明這一點:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         WSDualHttpBinding binding = new WSDualHttpBinding();
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

 

下面列出了輸出的所有繫結元素,從中可以看出TransactionFlowBindingElement對WS-Transactions的支援;ReliableSessionBindingElement對WS-RM的支援;SymmetricSecurityBindingElement對WS-Security的支援;這些繫結元素和TextMessageEncodingBindingElement、HttpTransportBindingElement都是與WsHttpbing共有的,而CompositeDuplexBindingElement和OneWayBindingElement則是WsDualHttpBinding不具有的,這兩個繫結元素實現雙工通訊和單項的資料包模式通訊。


對於WsHttpbing和WsDualHttpBinding的比較,還有一點值得注意的是在預設情況下,WsHttpbing並沒有ReliableSessionBindingElement,也就是說在預設的情況下,WsHttpbing並不支援可靠會話,而對於基於雙工通訊的WsDualHttpBinding,可靠會話則是必須的。至於WsDualHttpBinding為何不支援基於傳輸的安全,原因也很簡單,因為HTTP協議下的傳輸安全通過HTTPS(SSL)實現,HTTPS依賴於一個真正意義上的Web站點,也就是隻有訪問一個真正意義上Web站點的資源的前提下,HTTPS才會有有意義。而對於雙工通訊來說,由於客戶端滿足這樣要求,所以從服務端回撥客戶端的傳輸安全是無法確保的。

雙工通訊需要一個雙工的通訊通道,但是屬性TCP/IP的讀者應該很清楚,HTTP協議僅僅是一個單純的請求/回覆通訊協議,也就是說基於HTTP的通訊通道不可以支援雙工通訊,那麼WsDualHttpBinding又是如果在HTTP傳輸協議上實現雙工通訊的呢?答案很簡單,WsDualHttpBinding採用了兩個HTTP通道。

四、NetTcpBinding

到此為止,我們一共介紹了三種型別的繫結。從對於傳輸協議的支援來看,它們都就是基於HTTP或者HTTPS的繫結;從對標準的支援看來,BasicHttpBinding提供對WS-BP

1.1的支援,WsHttpBinding和WsDualHttpBinding則對WS-*新的協議提供很好的支援,比如WS-Transactions、WS-Reliable

Messaging、WS-Security等等;從訊息編碼的角度來看,它們均支援基於純文字的訊息編碼和MTOM編碼。這些屬性都決定了這三種繫結具有較好的互操作性,也就是說,對於此三種繫結的應用並不限於對於基於.NET平臺應用的互動,如果通過這些繫結寄宿我們的服務,其他平臺的客戶端可以呼叫我們的服務,同理我們也可以利用基於這些繫結的客戶端訪問其他非.NET平臺的Web服務,只要對方支援相應的標準。

接下來我們要介紹的另外三種繫結,相比之下就不具有如此好的互操作性,它們只能應用於單純的WCF客戶端和服務之間的互動。它們基於不同的傳輸協議,我們先來介紹基於TCP傳輸協議的NetTcpBinding。

我們照例採用列出繫結元素列表的方式分析繫結的特性,我們先通過下面的程式碼看看一個採用預設建構函式建立的NetTcpBinding物件會包含哪些綁丁元素。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         NetTcpBinding binding = new NetTcpBinding();
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

程式執行後,下面4個繫結元素會被先後輸出。我們借來分析一個NetTcpBinding物件在預設的情況下具有哪些特性:TcpTransportBindingElement表明採用TCP作為傳輸協議;WindowsStreamSecurityBindingElement提供基於Windows憑證的傳輸安全;BinaryMessageEncodingBindingElement實現基於二進位制的訊息編碼;TransactionFlowBindingElement則提供對事務流轉的支援。


上面涉及的四個繫結元素,除了WindowsStreamSecurityBindingElement,相信有了前面的介紹,讀者不會感到陌生。在這裡我們來簡單討論一下WindowsStreamSecurityBindingElement。WindowsStreamSecurityBindingElement繼承自System.ServiceModel.Channels.StreamUpgradeBindingElement,StreamUpgradeBindingElement是一種特殊的繫結元素。前面我們講了,繫結元素的使命在於對相應通道的建立,而StreamUpgradeBindingElement的特別之處在於它並會參與通道的建立。StreamUpgradeBindingElement一般應用於基於流的傳輸(Stream
Oriented
Transport),比如TCP、命名管道等等。它一般位於TransportBindingElement之上,在傳輸層基礎上提供進一步的升級處理(Transport

Upgrade),比如安全加密、壓縮等等。WindowsStreamSecurityBindingElement在這裡的提供基於Windows客戶端憑證的傳輸安全,與之相對的,還有一個System.ServiceModel.Channels.SslStreamSecurityBindingElement,提供基於SSL的傳輸安全。如果我們將繫結的客戶端憑證的型別改成Certificate或者None,SslStreamSecurityBindingElement將會被採用:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         NetTcpBinding binding = new NetTcpBinding();
   6:         binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
   7:         ListAllBindingElements(binding);
   8:     }
   9: }

WindowsStreamSecurityBindingElement將會被SslStreamSecurityBindingElement替換:

 

除了對傳輸安全模式的支援(預設),NetTcpBinding也提供對訊息安全模式提供支援,比如下面的程式碼中,再呼叫建構函式的時候直接將安全模式型別指定為:SecurityMode.Message。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         NetTcpBinding binding = new NetTcpBinding(SecurityMode.Message);
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

實際上,如果採用訊息安全模式,SymmetricSecurityBindingElement將會新增進來實現基於訊息級別的簽名、加密安全措施。這也可以從下面的輸出結果看出來:

 


除了單純的傳輸安全模式和訊息模式之外,NetTcpBinding還支援一種混合的安全模式,該模式的SecurityMode列舉值表示為:SecurityMode.TransportWithMessageCredential。該模式通過傳輸安全保障資料的一致性和保密性,通過訊息安全提供身份驗證。關於不同種類的安全模式,將在“安全”一章中進行詳細講解。SslStreamSecurityBindingElement和TransportSecurityBindingElement一起提供該模式的安全,如下面的程式碼所示:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         NetTcpBinding binding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

輸出結果:


和WsHttpBinding一樣,NetTcpBinding也提供對可靠會話的支援,以保障資料包或者訊息的可靠、有序傳遞。不過與WsHttpBinding的實現機制不同的是,基於NetTcpBinding是採用TCP協議固有的可靠傳輸機制,比如訊息確認機制、重發機制等等。下面的程式碼,通過ReliableSession.Enabled屬性讓繫結實現對可靠會話的支援:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         NetTcpBinding binding = new NetTcpBinding();
   6:         binding.ReliableSession.Enabled = true;
   7:         ListAllBindingElements(binding);
   8:     }
   9: }

和WsHttpBinding一樣,通過ReliableSessionBindingElement實現對可靠會話的支援:


由於NetTcpBinding採用TCP作為傳輸協議,所以它一般只應用於Intranet中;由於採用二進位制的訊息編碼方式,在效能上較之基於文字的編碼會有較大的提高;此外,由於和HTTP協議不同,TCP本身就是一個基於雙工通訊的協議,所以和WsDualBinding一樣可以用於基於雙工訊息交換模式的WCF應用中。

五、 NetNamedPipeBinding

NetNamedPipeBinding,顧名思義,就是基於命名管道傳輸的繫結。命名管道本身可以支援跨機器的通訊,而在WCF中對NetNamedPipeBinding作了更加嚴格的限制,使其只能用於同一臺機器的跨程式通訊(IPC)。所以在所有的繫結中,NetNamedPipeBinding將是效能最好的繫結型別。

我們照例通過分析繫結元素的方式來理解繫結本身的特性與能力。先通過下面的程式碼列出NetNamedPipeBinding預設的繫結元素:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         NetNamedPipeBinding binding = new NetNamedPipeBinding();
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

從輸出的繫結元素集合,我們可以得出這樣的結論:NamedPipeTransportBindingElement實現了基於命名管道的傳輸;WindowsStreamSecurityBindingElement提供了基於Windows憑證的傳輸安全保障;BinaryMessageEncodingBindingElement實現了基於二進位制的訊息編碼;而TransactionFlowBindingElement則為事務流轉提供支援。


由於NetNamedPipeBinding的特殊性(提供基於IPC的通訊),所以決定了它的一些相關的特性:僅僅支援傳輸模式的安全(實際上訊息安全模式在IPC場景下已經沒有意義);客戶端憑證之限於Windows。我們可以將安全模式設為None,使其不採用任何安全模式:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
   6:         ListAllBindingElements(binding);
   7:     }
   8: }

 

這樣,WindowsStreamSecurityBindingElement將從繫結元素集合中剔除:


除了上述的五種繫結型別,WCF中還定義了其他一些繫結,比如NetMsmqBinding、MsmqIntegrationBinding、WebHttpBinding等等,將會在具體設計到這些特殊的繫結的章節中介紹。

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
如果你想及時得到個人撰寫文章以及著作的訊息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。


相關文章