天天看點

WCF4.0新特性體驗(8):自定義綁定實作位元組流編碼(ByteStream)

今天繼續學習WCF4.0新特性體驗(8):自定義綁定實作位元組流編碼,也就是簡化位元組流編碼的新特性,Simple byte stream encoding )。WCF4.0之前的版本中已經提供了三種編碼器:Text、Binary 和 MTOM 消息編碼器。但是有些時候我們想傳遞最原始的二進制資料,不進行任何的包裝,怎麼才能實作呢?WCF4.0中給出了一種新的編碼器。今天我們就來學習如何使用這個新的編碼器。本節内容會對WCF消息編碼器、自定義綁定也會做簡要介紹。

【1】WCF編碼器:

 我們知道在WCF4.0以前的版本中已經提供了三種編碼器:Text、Binary 和 MTOM 消息編碼器。當然我們也可以實作自定義編碼器。Text消息編碼器同時支援純 XML 編碼和 SOAP 編碼。Text消息編碼器的純 XML 編碼模式稱為 POX(“Plain Old XML”)編碼器,以便與基于文本的 SOAP 編碼進行區分。關于三種編碼器類型如下:

編碼器類型

描述

<a href="http://msdn.microsoft.com/zh-cn/library/system.servicemodel.channels.textmessageencodingbindingelement.aspx" target="_blank">TextMessageEncodingBindingElement</a>

文本消息編碼器,同時支援純 POX 編碼和 SOAP 編碼。使用文本消息編碼器可以與非 WCF 終結點互動操作。

<a href="http://msdn.microsoft.com/zh-cn/library/system.servicemodel.channels.binarymessageencodingbindingelement.aspx" target="_blank">BinaryMessageEncodingBindingElement</a>

二進制消息編碼器,使用精簡二進制格式優化通信, WCF 提供的所有編碼器中性能最佳的編碼器。

<a href="http://msdn.microsoft.com/zh-cn/library/system.servicemodel.channels.mtommessageencodingbindingelement.aspx" target="_blank">MTOMMessageEncodingBindingElement</a>

綁定元素,指定使用 MTOM 編碼的消息的字元編碼和消息版本。MTOM 編碼以文本形式傳輸大多數 XML,但是按原樣傳輸較大的二進制資料塊。就效率而言, MTOM 介于在文本編碼器(最慢)和二進制編碼器(最快)之間。

    這些是目前我們使用的主要的三種消息編碼器類型。但無論什麼消息編碼器,它們都是MessageEncodingBindingElement 類的子類,必須實作WriteMessage和ReadMessage方法。這兩個方法是消息編碼的核心實作。《WCF技術内幕》綁定有比較深入的分析。它們的作用如下:

WriteMessage,此方法把Message 對象資料寫入到Stream 對象。

ReadMessage,此方法采從Stream 對象讀取資料,并建構一個新的Message 對象。

【2】位元組流編碼:

  WCF4.0提供了一個新的編碼器類型:ByteStreamMessageEncodingBindingElement 。它也是MessageEncodingBindingElement的子類型,

public sealed class ByteStreamMessageEncodingBindingElement : MessageEncodingBindingElement 

{......}

但是,沒有定義相對應的綁定支援這個編碼,是以,如果在WCF4.0中,我們想使用位元組流編碼機制,必須自己實作自定義綁定。也就是Creating Custom Binding。

【3】自定義綁定:

      這裡我們先要了解自定義綁定的概念,然後再來介紹如何通過自定義綁定來使用位元組流編碼機制。雖然系統提供了許多的綁定,但是WCF架構允許使用者自己定義特定的綁定類型。

  我們知道綁定由許多不同的綁定元素(BindingElement)組成,這些具有不同功能的綁定元素(BindingElement)累加起來,組成了一個具有完整功能的綁定(Binding)。

  Binding按照功能劃分,可以看到有如下幾個主要層次:事務、可靠性、安全性、編碼和傳輸。

綁定元素

必需

事務

TransactionFlowBindingElement

可靠性

ReliableSessionBindingElement

安全性

SecurityBindingElement

編碼

文本、二進制、消息傳輸優化機制 (MTOM)、位元組流、自定義

傳輸

TCP、HTTP、HTTPS、命名管道(也稱為 IPC)、對等 (P2P)、消息隊列(也稱為 MSMQ)、自定義

  這裡隻有編碼和傳輸層元素是必須的,其它的根據綁定要實作的功能來選擇是否添加進來。我們可以檢視一下常見綁定的綁定元素(BindingElement)組成。例如BasicHttpBinding和NetTcpBinding。它們的綁定元素構造如下:

 我們能看到為什麼BasicHttpBinding的功能如此簡單,并且支援Text編碼的。而NetTcpBinding相比起來功能更加複雜。比如支援事務、可靠性會話、安全等特性的。這個在《WCF技術内幕》綁定一章裡做過詳細的介紹。

【4】自定義綁定實作簡化位元組流編碼:

    下面我們來講解一下代碼的實作過程,這個實作的過程有點複雜。因為要通過自定義綁定來使用ByteStream編碼,還要注意一些限制,必須隻能使用http傳輸。另外還要使用消息契約MessageContract。過程就有些複雜,可能在實際的項目裡,使用的時候,大家都會考慮這個問題。隻能等WCF在下一個版本裡給出正式的支援在使用,也許會簡單很多,也許WCF5.0裡會出現一個新的ByteStreamHttpBinding。這樣開發的時候就會簡單很多。目前我們隻能通過自己的代碼實作。下面我們來分布介紹實作的過程。

【4.1】服務契約:

      服務契約裡我們定義了一個操作為UploadImage。代碼如下:

//1.服務契約 

        [ServiceContract] 

        public interface IWCFService 

        { 

                //操作契約 

                [OperationContract(Action = "*", ReplyAction = "*")] 

                Message UploadImage(Message request); 

        }

       服務實作的這個操作,它會再把原始的圖檔傳回給用戶端。通過Message來完成。接受用戶端消息,并傳回消息給用戶端,消息體裡就是圖檔的資料。也就是放在&lt;Binary&gt;圖檔資料&lt;/Binary&gt;節點裡的。代碼如下:

//2.服務類,繼承接口。實作服務契約定義的操作 

        public class WCFService : IWCFService 

                //實作接口定義的方法 

                public Message ProcessRequest(Message request) 

                { 

                        Console.WriteLine("接受圖檔資料"); 

                        //設定傳回消息的屬性 

                        HttpResponseMessageProperty httpResponseProperty = new HttpResponseMessageProperty(); 

                        httpResponseProperty.Headers.Add("Content-Type", "application/octet-stream"); 

                        request.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProperty); 

                        //列印時間 

                        Console.WriteLine("傳回圖檔資料 {0}", DateTime.Now.ToLongTimeString()); 

                        return request; 

                } 

     這裡的代碼很簡單,就是對請求消息做了一下簡單地轉換,傳回給客戶單,我們隻監控消息的傳回時間。

【4.2】自定義綁定:

       這裡我們就假定這個綁定的名字為ByteStreamHttpBinding。這個綁定隻有兩個綁定元素,一個是負責編碼的ByteStreamMessageEncodingBindingElement ,另外一個就是負責傳輸的HttpTransportBindingElement。但是要設定一些屬性。比如最大傳輸消息大小等等。代碼如下:

// Create a custom binding containing two binding elements 

                         //建立自定義綁定,隻能使用HttpTransportBindingElement,并且MessageVersion為None 

                         ByteStreamMessageEncodingBindingElement byteStreamBindingElement = new ByteStreamMessageEncodingBindingElement(); 

                         byteStreamBindingElement.ReaderQuotas.MaxArrayLength = 900000; 

                         byteStreamBindingElement.ReaderQuotas.MaxBytesPerRead = 4096; 

                         byteStreamBindingElement.ReaderQuotas.MaxDepth = 64; 

                         byteStreamBindingElement.ReaderQuotas.MaxStringContentLength = 900000; 

                         byteStreamBindingElement.ReaderQuotas.MaxNameTableCharCount = 900000; 

                         HttpTransportBindingElement transport = new HttpTransportBindingElement(); 

                         transport.TransferMode = TransferMode.Streamed; 

                         transport.MaxReceivedMessageSize = 900000; 

                         transport.MaxBufferSize = 900000; 

                         CustomBinding binding = new CustomBinding(byteStreamBindingElement, transport);

      這個自定義綁定的實作,也可以通過配置檔案來完成。另外也可以封裝在一個單獨的綁定類型ByteStreamHttpBinding。以便重複使用這些代碼。

【4.3】宿主:

       宿主使用自定義的綁定來托管WCF服務。代碼如下:

CustomBinding binding = new CustomBinding(byteStreamBindingElement, transport); 

                        // // Add an endpoint using that binding 

                        ////使用自定義綁定增加一個終結點 

                         host.AddServiceEndpoint(typeof(WCFService.IWCFService), binding, "ByteStreamWCFService"); 

                            //判斷是否以及打開連接配接,如果尚未打開,就打開偵聽端口 

                                if (host.State !=CommunicationState.Opening) 

                                host.Open(); 

                                //顯示運作狀态

【4.4】用戶端:

       用戶端這裡需要通過Message類型來設定消息。首先我們會從一個犀利哥的圖檔中讀取資料,onst string TestFileName = "./http://www.cnblogs.com/xilige.jpg";這裡處理消息體元素生成工作的就是ByteStreamBodyWriter類型,他繼承自抽象類BodyWriter,重寫了void OnWriteBodyContents(XmlDictionaryWriter writer)方法。這個機制是消息序列化的一個重要步驟。writer會控制每個消息體元素的生成工作。ByteStreamBodyWriter的實作代碼如下:

public class ByteStreamBodyWriter : BodyWriter 

                string testFileName; 

                public ByteStreamBodyWriter(string testFileName) 

                        : base(false) 

                        this.testFileName = testFileName; 

                protected override void OnWriteBodyContents(XmlDictionaryWriter writer) 

                        //生成初始節點&lt;Binary&gt; 

                        writer.WriteStartElement("Binary"); 

                        //寫資料 

                        FileStream fs = new FileStream(this.testFileName, FileMode.Open); 

                        byte[] tmp = new byte[fs.Length]; 

                        fs.Read(tmp, 0, tmp.Length); 

                        writer.WriteBase64(tmp, 0, (int)tmp.Length); 

                        //生成結束标簽&lt;/Binary&gt; 

                        writer.WriteEndElement(); 

                        fs.Close(); 

      這裡用戶端代碼就是生成一個消息,消息體裡讀取的是犀利哥的圖檔,然後使用自定義綁定建立通道發送消息。代碼如下:

const string TestFileName = "./http://www.cnblogs.com/xilige.jpg"; 

                        //ByteStreamBodyWriter writer = new ByteStreamBodyWriter(TestFileName); 

                        Message message = Message.CreateMessage(MessageVersion.None, "*", new ByteStreamBodyWriter(TestFileName)); 

                        HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty(); 

                        httpRequestProperty.Headers.Add("Content-Type", "application/octet-stream"); 

                        message.Properties.Add(HttpRequestMessageProperty.Name, httpRequestProperty); 

//// Create a channel using that binding 

                        ////建立通道 

                        EndpointAddress address = new EndpointAddress("http://localhost:8000/ByteStreamWCFService"); 

                        ChannelFactory&lt;IWCFService&gt; channelFactory = new ChannelFactory&lt;IWCFService&gt;(binding, address); 

                         IWCFService channel = channelFactory.CreateChannel(); 

                         Console.WriteLine("Client calling service"); 

                         Message result = channel.UploadImage(message);

       基本就是這個過程。

【4.5】運作結果:

       我們可以啟動宿主,然後單步執行用戶端代碼,看看運作的結果,截圖如下:

我們看到用戶端上傳了一個圖檔,服務端收到以後又傳回給用戶端,犀利哥的這個照片大小為41.474K。并且資料是放在消息體體的節點&lt;Binary&gt;裡。

【5】總結:

     這個新的特性,被稱為位元組流編碼的特性,有點讓人無奈,功能是好功能,但是隻提供了一個綁定元素,如果我們要使用的話必須自己實作自定義綁定。這個對于大多數開發者來說,可能有點複雜。這裡還有幾點需要值得注意,就是:

(1)位元組流編碼ByteStreamMessageEncodingBindingElement目前WCF4.0裡隻能通過自定義綁定實作。

(2)ByteStreamMessageEncodingBindingElement 隻能和Http傳輸結合,不支援其他的傳輸。

(3)與流傳輸一樣,位元組流不支援可靠性會話,也不支援消息安全。因為資料交大。加密解密處理會耗費較多的資源。

(5)Message類型設定的一個屬性"application/octet-stream" ,是是與MIME附件規範,這個表示消息内容是二進制檔案。

(6)它與二進制編碼器不同的是,二進制編碼器是對消息資料做精簡二進制編碼,而位元組流編碼則不同,可以傳遞最原始的二進制資料。

 參考資料:

5.《WCF技術内幕》:綁定

 本文轉自 frankxulei 51CTO部落格,原文連結:http://blog.51cto.com/frankxulei/320272,如需轉載請自行聯系原作者