WSE3.0框架提供了數據優化傳輸機制,WSE3.0構建Web服務安全(4):MTOM消息傳輸優化和文件上傳、下載疑問裡進行了介紹。WCF同樣也提供了流操作來支持大數據對象的傳輸和處理優化機制,今天我們WCF分布式開發步步為贏系列的(4):使用流操作(Streaming Operations)優化傳輸。本節會詳細介紹流操作的相關概念、編程實現過程,以及實際開發過程中需要主要的一些問題。本節結構:【1】流處理的概念【2】流處理的特點【3】示例代碼分析【4】總結。最後上傳本文的示例代碼。
Streaming,本文翻譯為流處理(張逸兄翻譯的Programming WCF Services一書裡把這個機制翻譯為“流操作”,不存在爭議。我選擇“流處理”一詞的意思,只是想把這個Streaming詞形象准確化,動名詞,流化處理、流處理,翻譯為流操作,初學者會誤會認為這個是一個服務操作。因為Streaming只是WCF內建的一個機制。而不是操作。)
我們首先來理解什麼是Streaming流處理。
【1】Streaming流處理的概念:
通常情況,客戶端和服務端進行交互,傳遞消息,都是放到接收端的緩存裡,待接收完畢後再進行處理。無論接收端是客戶端還是服務端都是如此。
【1.1】要解決的問題:
當客戶端調用服務時,要阻塞客戶單進程,直到消息發送完畢,服務端才開始處理數據,然後是返回處理完畢的結果給客戶端,客戶端接收完畢,才能解除阻塞。這樣帶來的問題是當消息傳遞的時間很短,相對處理時間可以忽略不計,不會影響系統服務的效率。但是要是消息數據很大,比如是圖片或者多媒體對象。每次傳輸時間相對較大,這樣接收端的等待時間過久,勢必每次阻塞都會很長,進程無法繼續執行。因而導致效率低下。
【1.2】Streaming流處理:
Streaming流處理就是WCF提供的主要針對大量消息數據處理的一種優化機制。WCF允許接收端通過通道接受消息的同時,啟動對消息數據的處理,這樣的過程稱為流傳輸模型。
【2】Streaming流處理的特點:
顯然對於處理大量的消息數據而言,流處理機制改善了系統的吞吐量和響應效率。
【2.1】流處理操作定義:
WCF的流處理機制需要使用.NET FrameWork定義的Stream類(它是FileStream, NetworkStream, MemoryStream 的父類)。流處理適用一下場景:
[ServiceContract]
interface IMyContract
{
[OperationContract]
Stream StreamReply1( );
[OperationContract]
void StreamReply2(out Stream stream);
[OperationContract]
void StreamRequest(Stream stream);
[OperationContract(IsOneWay = true)]
void OneWayStream(Stream stream);
}
它可以做為返回數據、參數、輸出參數的類型。當然也可以作為單調服務的操作參數。這裡使用的參數必須是可序列化的,例如MemoryStream。而FileStream不支持序列化因而不能作為參數或者返回數據的類型。
【2.2】流處理與綁定協議:
流處理機制在特定的綁定協議中才能使用,目前是BasicHttpBinding, NetTcpBinding, 和NetNamedPipeBinding 支持流處理模型。但是在默認情況下,WCF禁止流處理模式。
流傳輸模式使用使用TransferMode進行配置,TransferMode為枚舉類型,其定義如下:
public enum TransferMode
{
// Summary:
// The request and response messages are both buffered.
Buffered = 0,
//
// Summary:
// The request and response messages are both streamed.
Streamed = 1,
//
// Summary:
// The request message is streamed and the response message is buffered.
StreamedRequest = 2,
//
// Summary:
// The request message is buffered and the response message is streamed.
StreamedResponse = 3,
}
只有Streamed模式支持2.1中列舉的流處理模式場景。除了直接在服務上配置屬性以外,我們還可以再服務的配置文件裡定義流傳輸模式。代碼如下:
<basicHttpBinding>
<binding name="basicHttpBinding" receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding name="netTcpBinding" receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
</binding>
</netTcpBinding>
此為托管宿主的配置文件,特定的綁定協議,可以配置其傳輸模式。
【2.3】注意:
流處理在使用http協議時,其默認消息長度是64K,如果希望增加數據長度,需要在配置文件裡重新設置。如: maxReceivedMessageSize="200000",具體代碼如下:
<basicHttpBinding>
<binding name="basicHttpBinding" receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
</binding>
</basicHttpBinding>
【3】示例代碼分析:
這裡測試的流處理機制,使用的是處理圖片的上傳於下載,分別使用Stream和其子類MemoryStream作為參數或者返回消息數據的類型。基本代碼演示的是流處理三種模式場景:
【3.1】服務端:
服務契約分別定義了下載數據和上傳數據,下載數據使用的類型MemoryStream,上傳數據參數類型是是Stream。具體代碼如下:
//1.服務契約
[ServiceContract( Namespace = "http://www.cnblogs.com/frank_xl/")]
public interface IWCFService
{
//操作契約,獲取數據流
[OperationContract]
MemoryStream DownLoadStreamData(string fileName);
//操作契約,輸出數據流
[OperationContract]
void DownLoadStreamDataOut(out MemoryStream stream, string fileName);
//操作契約,上載數據流,單向操作的消息轉換為數據流
[OperationContract(IsOneWay=true)]
void UpLoadStreamData(Stream stream);
}
//2.服務類,繼承接口。實現服務契約定義的操作
public class WCFService : IWCFService
{
//1實現接口定義的方法,下載文件數據流
public MemoryStream DownLoadStreamData(string fileName)
{
// Stream stream =
byte[] file = new byte[200000];
String filePath = AppDomain.CurrentDomain.BaseDirectory + @"\" + fileName;
file = File.ReadAllBytes(filePath);
MemoryStream memoryStream = new MemoryStream(file);
return memoryStream;
}
//2實現接口定義的方法,下載文件數據流
public void DownLoadStreamDataOut(out MemoryStream stream, string fileName)
{
// Stream stream =
byte[] file = new byte[200000];
String filePath = AppDomain.CurrentDomain.BaseDirectory + @"\" + fileName;
file = File.ReadAllBytes(filePath);
MemoryStream memoryStream = new MemoryStream(file);
stream = memoryStream;
}
//3實現接口定義的方法,上傳文件數據流
public void UpLoadStreamData(Stream stream)
{
// codes here to deal with the stream Stream stream =
Console.WriteLine("The Stream length is {0}",stream.Length);
}
}
【3.2】托管宿主:
我們分別使用basicHttpBinding和netTcpBinding定義了兩個服務終結點,這裡不要忘記設置最大接受消息數據大小maxReceivedMessageSize="200000",如果設置較小會導致接受數據超過設定的錯誤。具體代碼如下:
<system.serviceModel>
<services>
<service behaviorConfiguration="WCFService.WCFServiceBehavior"
name="WCFService.WCFService">
<endpoint
address="http://localhost:8002/WCFService"
binding="basicHttpBinding"
contract="WCFService.IWCFService">
</endpoint>
<endpoint
address="net.tcp://localhost:8004/WCFService"
binding="netTcpBinding"
contract="WCFService.IWCFService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8001/"/>
<add baseAddress="net.tcp://localhost:8003/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WCFService.WCFServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBinding" receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding name="netTcpBinding" receiveTimeout="10:10:10" transferMode="Streamed" maxReceivedMessageSize="200000">
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
【3.3】客戶端:
客戶端分別測試數據的上傳和下載,使用不同的方法。這裡的測試目前
【4】總結:
本文介紹了WCF流處理模型,但是實現代碼出現問題。
(1)作為WCF的流處理機制,確實為我們的大規模消息數據傳輸提供了理想的機制,提高了系統的效率和響應速度。
(2)Stream作為.net類庫的內部類型,在.net平台上使用來說較為方便,但是與異構平台的交互勢必受到諸多限制,也違背了WCF跨平台的初衷。
(3) 我在調試這個示例代碼的過程中遇到了幾個錯誤,基本都整理出來放到WCF分布式開發常見錯誤裡了。使用netTcpBinding綁定進行數據傳輸的時候,一個很有價值的錯誤就是:the socket connection was aborted. this could be caused by an error processing your message or a receive timeout being exceeded by the remote host ...這個錯誤我google國內和國外的一些資料,但是解決的辦法都是更換協議。其實是一種很無奈的措施,目前我還沒有找出更好的解決辦法就是基於netTcpBinding。表面的原因是服務處理超時。但是具體的錯誤信息沒有這樣簡單。我更換協議以後其他的流服務操作調用出了問題。所以這個只能針對特定的操作有幫助。我把這個錯誤收集起來供大家參考。也希望發揮大家的作用把這個問題解決。
也許這個錯誤應該反映給WCF的開發小組,無論國內還是國外的技術論壇,包括MSDN都有人遇到這樣的問題,而沒有一個最佳的解決方案。這裡我對流處理示例代碼分別打包,目前都有問題。
<1>流處理機制示例1裡代碼測試上傳文件,成功,下載文件錯誤。
http://files.cnblogs.com/frank_xl/WCFServiceStreamingFrankXuLei.rar
<2>這裡使用的是字節數組,測試下載文件,下載文件是成功的,上傳文件失敗。
http://files.cnblogs.com/frank_xl/WCFServiceStreamingByteArrayFrankXuLei.rar
兩個失敗的原因都是一樣,套接字中斷,連接超時。WCF分布式開發步步為贏(10):請求應答(Request-Reply)、單向操作(One-Way)、回調操作(Call Back).,我進行了整理,也查找了國外的論壇,沒有找到理想的解決辦法。我已經嘗試了修改接受時間的限制,但是不起作用。我會繼續關注這個問題,也希望有興趣的朋友補充。MSDN論壇上有人提供了解決的方法,但是不理想,更換協議。希望微軟WCF的開發、測試小組早日注意這個問題。