- WCF技術(shù)剖析(卷1)
- 蔣金楠
- 3041字
- 2018-12-27 11:32:54
3.5.2 綁定揭秘
前面一直在談?wù)撔诺馈⑿诺拦芾砥鳌⑿诺辣O(jiān)聽(tīng)器、信道工廠和綁定元素,現(xiàn)在我們進(jìn)入本章的主題——綁定。不過(guò)由于前面的鋪墊已經(jīng)很足了,綁定本身反而沒(méi)有太多可大書(shū)特書(shū)的地方。如果從結(jié)構(gòu)上給綁定下個(gè)定義,那么我的定義很簡(jiǎn)單:“綁定是綁定元素的有序集合”。
綁定是綁定元素的有序集合
由于綁定的終極目標(biāo)是實(shí)現(xiàn)對(duì)信道棧的創(chuàng)建,而對(duì)于一個(gè)信道棧來(lái)說(shuō),信道的構(gòu)成和次序決定著該信道棧在最終消息通信中的特性與能力。綁定元素又決定著信道的創(chuàng)建,所以綁定對(duì)象本身的特性與能力由自身包含的所有綁定元素及這些綁定元素之間的先后次序決定。
當(dāng)我們須要判斷某一個(gè)綁定類(lèi)型是否支持某種特性的時(shí)候,只須查看該綁定是否具有與此特性相關(guān)的綁定元素。比如,我們要判斷某種綁定是否支持某種類(lèi)型的傳輸協(xié)議,只須看看構(gòu)成該綁定的傳輸綁定元素就可以了,WsHttpBinding的傳輸綁定元素是HttpTransportBindingElement,所以它只能支持基于HTTP或HTTPS的傳輸協(xié)議;如果須要確定某種類(lèi)型的綁定是否支持事務(wù)的流轉(zhuǎn),只須查看該綁定的綁定元素集合中是否包含TransactionFlowBindingElement就可以了。
在WCF中,所有的綁定都直接或間接繼承自抽象基類(lèi):System.ServiceModel.Channels. Binding,現(xiàn)在來(lái)簡(jiǎn)單地分析一下這個(gè)基類(lèi)。首先,Binding實(shí)現(xiàn)了接口IDefaultCommunication-Timeouts。4個(gè)默認(rèn)的超時(shí)時(shí)限可以通過(guò)編程的方式顯式指定,也可以通過(guò)配置的方式進(jìn)行設(shè)置。他們的默認(rèn)值分別為:OpenTimeout和CloseTimeout為1分鐘,而SendTimeout和ReceiveTimeout為10分鐘。
public abstract class Binding : IDefaultCommunicationTimeouts { //其他成員 public TimeSpan OpenTimeout { get; set; } public TimeSpan CloseTimeout { get; set; } public TimeSpan SendTimeout { get; set; } public TimeSpan ReceiveTimeout { get; set; } }
對(duì)于Binding,最為重要的就是如何構(gòu)建組成該綁定對(duì)象的所有綁定元素集合。基于綁定元素的創(chuàng)建,通過(guò)抽象方法CreateBindingElements實(shí)現(xiàn),所有具體的綁定類(lèi)型均須要實(shí)現(xiàn)該方法。
public abstract class Binding : IDefaultCommunicationTimeouts { //其他成員 public abstract BindingElementCollection CreateBindingElements(); }
由于信道管理器棧創(chuàng)建相應(yīng)的信道棧,而綁定創(chuàng)建信道管理器棧,因此在Binding中定義了一系列BuildChannelFactory<TChannel>和BuildChannelListener<TChannel>方法重載,用于創(chuàng)建信道工廠和信道監(jiān)聽(tīng)器。此外,和BindingElement一樣,CanBuildChannelFactory<TChannel>和CanBuildChannelListener<TChannel>用于檢測(cè)綁定對(duì)象創(chuàng)建信道工廠和信道監(jiān)聽(tīng)器的能力:
public abstract class Binding : IDefaultCommunicationTimeouts { //其他成員 public IChannelFactory<TChannel> BuildChannelFactory<TChannel>(params object[] parameters); public virtual IChannelFactory<TChannel> BuildChannelFactory<TChannel> (BindingParameterCollection parameters); public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (params object[] parameters) where TChannel : class, IChannel; public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (BindingParameterCollection parameters) where TChannel : class, IChannel; public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (Uri listenUriBaseAddress, params object[] parameters) where TChannel : class, IChannel; public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (Uri listenUriBaseAddress, BindingParameterCollection parameters) where TChannel : class, IChannel; public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (Uri listenUriBaseAddress, string listenUriRelativeAddress, params object[] parameters) where TChannel : class, IChannel; public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (Uri listenUriBaseAddress, string listenUriRelativeAddress, BindingParameterCollection parameters) where TChannel : class, IChannel; public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode, params object[] parameters) where TChannel : class, IChannel; public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode, BindingParameterCollection parameters) where TChannel : class, IChannel; public virtual bool CanBuildChannelFactory<TChannel> (BindingParameterCollection parameters); public bool CanBuildChannelFactory<TChannel>(params object[]parameters); public bool CanBuildChannelListener<TChannel>(params object[]parameters) where TChannel : class, IChannel; public virtual bool CanBuildChannelListener<TChannel> (BindingParameterCollection parameters) where TChannel : class, IChannel; }
案例演示:如何創(chuàng)建自定義綁定
在之前一系列的案例演示中,我們先后創(chuàng)建了自定義的信道、信道監(jiān)聽(tīng)器、信道工廠與綁定元素。這些對(duì)象最終只有通過(guò)具體的綁定對(duì)象才能應(yīng)用到WCF的運(yùn)行環(huán)境中,接下來(lái),就演示如何通過(guò)自定義綁定應(yīng)用創(chuàng)建綁定的元素。
1.自定義數(shù)據(jù)報(bào)綁定
為了簡(jiǎn)單起見(jiàn),對(duì)于我們自定義的綁定,僅僅包含3個(gè)必須的綁定元素:傳輸綁定元素和消息編碼綁定元素,外加自定義的綁定元素。對(duì)于傳輸,我們采用基于HTTP協(xié)議的HttpTransportBindingElement,而對(duì)應(yīng)消息編碼,則采用基于文本編碼方式的TextMessageEncodingBindingElement。我們將自定義的綁定命名為SimpleDatagramBinding。
namespace Artech.CustomChannels { public class SimpleDatagramBinding:Binding { private TransportBindingElement _transportBindingElement = new HttpTransportBindingElement(); private MessageEncodingBindingElement _messageEncodingBindingElement = new TextMessageEncodingBindingElement(); private SimpleDatagramBindingElement _SimpleDatagramBindingElement = new SimpleDatagramBindingElement(); public override BindingElementCollection CreateBindingElements() { BindingElementCollection elemens=new BindingElementCollection(); elemens.Add(this._SimpleDatagramBindingElement); elemens.Add(this._messageEncodingBindingElement); elemens.Add(this._transportBindingElement); return elemens.Clone(); } public override string Scheme { get { return this._transportBindingElement.Scheme; } } } }
對(duì)于整個(gè)SimpleDatagramBinding的定義,僅僅重寫(xiě)了CreateBindingElements方法和Scheme屬性。CreateBindingElements方法返回一個(gè)表示綁定元素集合的BindingElement-Collection對(duì)象,在該集合中,包含3種類(lèi)型的綁定元素,從上到下的順序分別為:SimpleDatagramBindingElement、MessageEncodingBindingElement和HttpTransportBinding-Element。Scheme是只讀屬性的,它返回HttpTransportBindingElement的Scheme:http。
2. 在WCF應(yīng)用中使用自定義綁定
既然自定義綁定已經(jīng)被創(chuàng)建出來(lái)了,我們先通過(guò)一個(gè)具體的WCF應(yīng)用例子來(lái)使用創(chuàng)建的SimpleDatagramBinding。WCF應(yīng)用照例采用典型的4層結(jié)構(gòu):Contracts、Services、Hosting和Clients(如圖3-15所示)。

圖3-15 案例解決方案結(jié)構(gòu)
先來(lái)介紹Contract和Service的定義,為了簡(jiǎn)單起見(jiàn),我們就定義一個(gè)DoSomething操作,服務(wù)與服務(wù)契約的定義如下所示。
using System.ServiceModel; namespace Artech.CustomChannels.Contracts { [ServiceContract(Namespace="http://www.artech.com/")] public interface IService { [OperationContract] void DoSomething(); } }
using System; using Artech.CustomChannels.Contracts; namespace Artech.CustomChannels.Services{ public class Service:IService { public void DoSomething() { Console.WriteLine("Done..."); } } }
在服務(wù)的寄宿程序中,通過(guò)調(diào)用ServiceHost對(duì)象的AddServiceEndpoint方法將終結(jié)點(diǎn)的綁定指定成自定義的綁定:SimpleDatagramBinding。
using System; using System.ServiceModel; using Artech.CustomChannels.Services; using Artech.CustomChannels.Contracts; namespace Artech.CustomChannels.Hosting { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(Service))) { host.AddServiceEndpoint(typeof(IService), new SimpleDatagramBinding(), "http://127.0.0.1:9999/service"); host.Open(); Console.Read(); } } } }
服務(wù)的客戶(hù)端實(shí)際將SimpleDatagramBinding對(duì)象和終結(jié)點(diǎn)地址作為參數(shù)創(chuàng)建ChannelFactory<IService>對(duì)象,最后通過(guò)該對(duì)象創(chuàng)建的服務(wù)代理進(jìn)行服務(wù)的調(diào)用。為了演示信道棧與客戶(hù)端(服務(wù)代理)之間的關(guān)系,我們先后創(chuàng)建了兩個(gè)服務(wù)代理對(duì)象,代碼如下。
using System; using System.ServiceModel; using System.ServiceModel.Channels; using Artech.CustomChannels.Contracts; namespace Artech.CustomChannels.Clients { class Program { static void Main(string[] args) { EndpointAddress address = new EndpointAddress ("http://127.0.0.1:9999/service"); using (ChannelFactory<IService> channelFactory = new ChannelFactory <IService>(new SimpleDatagramBinding(), address)) { //創(chuàng)建第一個(gè)服務(wù)代理 IService proxy = channelFactory.CreateChannel(); proxy.DoSomething(); proxy.DoSomething(); (proxy as ICommunicationObject).Close(); //創(chuàng)建第二個(gè)服務(wù)代理 proxy = channelFactory.CreateChannel(); proxy.DoSomething(); proxy.DoSomething(); (proxy as ICommunicationObject).Close(); } Console.Read(); } } }
本案例的目的不僅僅在于演示如何在WCF應(yīng)用中使用自定義綁定,還想通過(guò)一個(gè)具體、鮮活的例子讓讀者認(rèn)識(shí)到在進(jìn)行服務(wù)寄宿、服務(wù)調(diào)用的過(guò)程中,綁定模型中的一系列對(duì)象:信道、信道監(jiān)聽(tīng)器、信道工廠及綁定元素之間是如何交互的。由于在自定義信道、信道監(jiān)聽(tīng)器、信道工廠及自定義綁定元素的大部分方法中都通過(guò)PrintHelper輸出了當(dāng)前執(zhí)行的方法名,所以可以根據(jù)輸出大體地看出整個(gè)執(zhí)行流程。
下面是在服務(wù)成功寄宿后(尚無(wú)任何服務(wù)調(diào)用)服務(wù)端的輸出結(jié)果。大體上可以從中認(rèn)識(shí)到服務(wù)寄宿過(guò)程中,服務(wù)端信道層是如何工作的。對(duì)于服務(wù)的每一個(gè)終結(jié)點(diǎn)(具有不同的監(jiān)聽(tīng)地址),WCF會(huì)遍歷綁定的所有綁定元素,并調(diào)用BuildChannelListener<TChannel>創(chuàng)建信道監(jiān)聽(tīng)器;信道監(jiān)聽(tīng)器開(kāi)啟之后,調(diào)用BeginAcceptChannnel創(chuàng)建信道。信道開(kāi)啟之后,信道監(jiān)聽(tīng)器監(jiān)聽(tīng)來(lái)自用戶(hù)的請(qǐng)求,并利用創(chuàng)建好的信道棧接收請(qǐng)求消息。
SimpleDatagramBindingElement.BuildChannelListener<TChannel> SimpleDatagramChannelListener^1.SimpleDatagramChannelListener SimpleDatagramChannelListener^1.OnOpen SimpleDatagramChannelListener^1.OnBeginAcceptChannnel SimpleDatagramChannelListener^1.OnEndAcceptChannnel SimpeReplyChannel.SimpeReplyChannel SimpleDatagramChannelListener^1.OnBeginAcceptChannnel SimpeReplyChannel.OnOpen SimpeReplyChannel.BeginTryReceiveRequest
如果執(zhí)行完客戶(hù)端的所有服務(wù)調(diào)用程序,客戶(hù)端將會(huì)具有如下的輸出。從中反映出的流程是這樣的:遍歷當(dāng)前終結(jié)點(diǎn)綁定對(duì)象的所有綁定元素,調(diào)用BuildChannelFactory<TChannel>創(chuàng)建信道工廠(此時(shí)ChannelFactory<IService>被創(chuàng)建);信道工廠正常開(kāi)啟后,通過(guò)CreateChannel方法創(chuàng)建信道(此時(shí)服務(wù)代理通過(guò)ChannelFactory<IService>對(duì)象創(chuàng)建);在通過(guò)信道進(jìn)行第一次調(diào)用(或者顯式調(diào)用服務(wù)代理的Open方法)時(shí),信道被開(kāi)啟,被開(kāi)啟的信道可以被多次用于服務(wù)調(diào)用。如果顯式關(guān)閉服務(wù)代理對(duì)象,基于它的客戶(hù)端信道棧也將被關(guān)閉。
SimpleDatagramBindingElement.BuildChannelFactory<TChannel> SimpleDatagramChannelFactory^1.SimpleDatagramChannelFactory SimpleDatagramChannelFactory^1.OnOpen SimpleDatagramChannelFactory^1.OnCreateChannel SimpleRequestChannel.SimpleRequestChannel SimpleRequestChannel.OnOpen SimpleRequestChannel.Request SimpleRequestChannel.Request SimpleRequestChannel.OnClose SimpleDatagramChannelFactory^1.OnCreateChannel SimpleRequestChannel.SimpleRequestChannel SimpleRequestChannel.OnOpen SimpleRequestChannel.Request SimpleRequestChannel.Request SimpleRequestChannel.OnClose
當(dāng)客戶(hù)端正常地執(zhí)行完畢,服務(wù)端會(huì)多了如下一些額外的輸出。通過(guò)這些輸出可以看出,雖然客戶(hù)端程序先后創(chuàng)建了兩個(gè)服務(wù)代理進(jìn)行服務(wù)調(diào)用,但是服務(wù)端用于接收請(qǐng)求消息的信道棧僅有一個(gè)。
SimpeReplyChannel.EndTryReceiveRequest SimpeReplyChannel.BeginTryReceiveRequest Done… SimpeReplyChannel.EndTryReceiveRequest SimpeReplyChannel.BeginTryReceiveRequest Done… SimpeReplyChannel.EndTryReceiveRequest SimpeReplyChannel.BeginTryReceiveRequest Done… SimpeReplyChannel.EndTryReceiveRequest SimpeReplyChannel.BeginTryReceiveRequest Done
3.自定義會(huì)話綁定
接下來(lái),我們創(chuàng)建一個(gè)自定義會(huì)話綁定,并應(yīng)用之前創(chuàng)建的會(huì)話綁定元素SimpleSessionBindingElement。CreateBindingElements方法返回的集合中包含3種類(lèi)型的綁定元素:
● SimpleSessionBindingElement:自定義會(huì)話綁定元素。
● BinaryMessageEncodingBindingElement:基于二進(jìn)制編碼的消息編碼綁定元素。
● TcpTransportBindingElement:基于TCP的傳輸綁定元素。
using System.ServiceModel.Channels; namespace Artech.CustomChannels { public class SimpleSessionBinding:Binding { private SimpleSessionBindingElement _simpleElement = new SimpleSessionBindingElement(); private MessageEncodingBindingElement _encodingElement = new BinaryMessageEncodingBindingElement(); private TransportBindingElement _transportBindingElement = new TcpTransportBindingElement(); public override BindingElementCollection CreateBindingElements() { BindingElementCollection elemens=new BindingElementCollection(); elemens.Add(this._simpleElement); elemens.Add(this._encodingElement); elemens.Add(this._transportBindingElement); return elemens.Clone(); } public override string Scheme { get { return this._transportBindingElement.Scheme; } } } }
為了比較數(shù)據(jù)報(bào)綁定與會(huì)話綁定在消息交換過(guò)程不同的表現(xiàn)行為,我們將SimpleSessionBinding應(yīng)用到上面的WCF應(yīng)用中,僅僅替換到服務(wù)端和客戶(hù)端綁定即可。
static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(Service))) { host.AddServiceEndpoint(typeof(IService), new SimpleSessionBinding(), "net.tcp://127.0.0.1:9999/service"); host.Open(); Console.Read(); } }
static void Main(string[] args) { EndpointAddress address = new EndpointAddress ("net.tcp://127.0.0.1:9999/service"); using (ChannelFactory<IService> channelFactory = new ChannelFactory<IService>(new SimpleSessionBinding(),address)) { //...... } }
在服務(wù)成功寄宿后,服務(wù)端輸出如下。這和數(shù)據(jù)報(bào)綁定的表現(xiàn)形式有所不同:數(shù)據(jù)報(bào)綁定在開(kāi)始監(jiān)聽(tīng)的時(shí)候就已經(jīng)通過(guò)信道監(jiān)聽(tīng)器創(chuàng)建了信道棧,而會(huì)話綁定在監(jiān)聽(tīng)的時(shí)候,信道棧還沒(méi)有被建立起來(lái)。
SimpleSessionBindingElement.BuildChannelListener<TChannel> SimpleSessionChannelListener^1.SimpleSessionChannelListener SimpleSessionChannelListener^1.OnOpen SimpleSessionChannelListener^1.OnBeginAcceptChannel
在客戶(hù)服務(wù)調(diào)用程序正常執(zhí)行后,客戶(hù)端會(huì)具有如下的輸出。這和前面例子中的輸出類(lèi)似,不同的僅僅是請(qǐng)求信道通過(guò)Request方法向服務(wù)端發(fā)送請(qǐng)求和接收回復(fù),雙工通信則通過(guò)調(diào)用Send和Receive發(fā)送和接收消息。
SimpleSessionBindingElement.BuildChannelFactory<TChannel> SimpleSessionChannelFactory^1.SimpleSessionChannelFactory SimpleSessionChannelFactory^1.OnOpen SimpleSessionChannelFactory^1.OnCreateChannel SimpleDuplexSessionChannel.SimpleDuplexSessionChannel SimpleDuplexSessionChannel.OnOpen SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.TryReceive SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.TryReceive SimpleDuplexSessionChannel.OnClose SimpleSessionChannelFactory^1.OnCreateChannel SimpleDuplexSessionChannel.SimpleDuplexSessionChannel SimpleDuplexSessionChannel.OnOpen SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.TryReceive SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.TryReceive SimpleDuplexSessionChannel.OnClose
會(huì)話綁定不同于數(shù)據(jù)報(bào)信道的工作機(jī)制主要通過(guò)服務(wù)端的輸出體現(xiàn),當(dāng)服務(wù)調(diào)用程序正常運(yùn)行時(shí),服務(wù)端多出了如下一段輸出。仔細(xì)分析輸出結(jié)果,你會(huì)發(fā)現(xiàn)對(duì)于會(huì)話信道棧的創(chuàng)建發(fā)生在成功監(jiān)聽(tīng)到調(diào)用請(qǐng)求的時(shí)候。會(huì)話信道棧與具體的客戶(hù)端綁定,當(dāng)關(guān)閉客戶(hù)端會(huì)話信道棧的時(shí)候,服務(wù)端的會(huì)話信道棧也會(huì)相應(yīng)關(guān)閉。
SimpleSessionChannelListener^1.OnEndAcceptChannel SimpleDuplexSessionChannel.SimpleDuplexSessionChannel SimpleSessionChannelListener^1.OnBeginAccesptChannel SimpleDuplexSessionChannel.OnOpen SimpleDuplexSessionChannel.BegingTryReceive SimpleDuplexSessionChannel.EndTryReceive Done... SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.BegingTryReceive SimpleDuplexSessionChannel.EndTryReceive Done... SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.BegingTryReceive SimpleDuplexSessionChannel.EndTryReceive SimpleDuplexSessionChannel.OnClose SimpleSessionChannelListener^1.OnEndAcceptChannel SimpleDuplexSessionChannel.SimpleDuplexSessionChannel SimpleSessionChannelListener^1.OnBeginAccesptChannel SimpleDuplexSessionChannel.OnOpen SimpleDuplexSessionChannel.BegingTryReceive SimpleDuplexSessionChannel.EndTryReceive Done... SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.BegingTryReceive SimpleDuplexSessionChannel.EndTryReceive Done... SimpleDuplexSessionChannel.Send SimpleDuplexSessionChannel.BegingTryReceive SimpleDuplexSessionChannel.EndTryReceive SimpleDuplexSessionChannel.OnClose
- 黑客攻防從入門(mén)到精通(實(shí)戰(zhàn)秘笈版)
- 新編Visual Basic程序設(shè)計(jì)上機(jī)實(shí)驗(yàn)教程
- OpenStack Cloud Computing Cookbook(Third Edition)
- Responsive Web Design with HTML5 and CSS3
- Spring Boot進(jìn)階:原理、實(shí)戰(zhàn)與面試題分析
- Node.js全程實(shí)例
- Learning Raspbian
- Clojure for Machine Learning
- 編程可以很簡(jiǎn)單
- Machine Learning for Developers
- Simulation for Data Science with R
- Oracle Data Guard 11gR2 Administration Beginner's Guide
- 精通Spring:Java Web開(kāi)發(fā)與Spring Boot高級(jí)功能
- Java EE架構(gòu)設(shè)計(jì)與開(kāi)發(fā)實(shí)踐
- 嵌入式C編程實(shí)戰(zhàn)