WCF入門(四)——流傳輸


當有大量的數據要傳輸(例如文件的上傳和下載)時,WCF的流模式是比較好的選擇,因為流模式不是全部傳輸完后才響應,而是一邊讀取一邊傳輸消息,改善了系統的吞吐量和響應效率。

但是,WCF對於Stream操作有一些限制:

1. 綁定的限制。由於低層協議特性限制,WCF的流模式只支持如下四種:

  • BasicHttpBinding
  • NetTcpBinding
  • NetNamedPipeBinding
  • WebHttpBinding

2. OperationContract接口的限制。若要對數據進行流處理,服務的 OperationContract 必須滿足兩個要求:

  • 最多只能有一個參數。
  • 參數和返回值的類型中至少有一個必須是 Stream, Message 或 IXmlSerializable。

3. 會話模式限制

  當InstanceContextMode設置為Single或PerSession的時,多個服務請求可能會使用同一數據通道。此時就無法實現並發下載,只能依次順序執行,而這往往不是我們所期望的結果。因此,對於使用流模式的服務,需要將InstanceContextMode設置為PerCall。

如下是幾個有效的示例:

    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface IstreamingSample
    {
        [OperationContract]
        Stream GetStream(string data);
        [OperationContract]
        bool UploadStream(Stream stream);
        [OperationContract]
        Stream EchoStream(Stream stream);
        [OperationContract]
        Stream GetReversedStream();
    }

如果有許多信息需要作為參數傳遞,可以使用MessageContract構造一個復雜點的消息。

    [MessageContract]
    public class UploadStreamMessage
    {
        [MessageHeader]
        public string id;
        [MessageBodyMember]
        public Stream data;
    }

這個參數的限制並不難理解:

  1. 一次請求-響應之間只發送一個數據包,本質上來說只能攜帶一個參數。
  2. 在緩沖模式下,系統可以把多個參數全部加載到內存中后,然后通過序列化的方式將其合並為一個數據包再發送,這樣看起來可以發送多個參數了。
  3. 流模式並不能使用這種方案,因此頂多只能發一個參數。要發多個參數,必須自己定義數據包格式(需要滿足 MessageContract ),將多個參數整合到一個參數中發送。

下面我就以一個文件上傳為例,簡單的演示一下如何實現流模式數據傳輸。

一. 服務器端修改配置

  1. 設置TransferMode。它支持四種模式(Buffered、Streamed、StreamedRequest、StreamedResponse),請根據具體情況設置成三種Stream模式之一。
  2. 修改MaxReceivedMessageSize。該值默認大小為64k,因此,當傳輸數據大於64k時,則拋出CommunicationException異常。(可以直接設置為int.max)
  3. 修改receiveTimeout sendTimeout。大數據傳送時間較長,需要修改這兩個值,以免傳輸超時。

    <basicHttpBinding>
        <binding name="BasicBinding" receiveTimeout="00:30:00" sendTimeout="00:30:00" maxReceivedMessageSize="104857600" transferMode="Streamed" />
    </basicHttpBinding>

二. 定義契約,並實現服務

這個接口很簡單,就是實現上傳一個文件:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        Task UploadFile(Stream stream);
    }

由於接口中除了Stream沒有其它的有限參數,我這里的實現也很簡單,只是直接把它保存為1.jpg。

    public class Service1 : IService1
    {
        public async Task UploadFile(Stream stream)
        {
            using (stream)
            using (var file = File.Create(@"R:\server\1.jpg"))
            {
                await stream.CopyToAsync(file);
            }
        }
    }

三. 訪問服務

客戶端的實現一如既往的簡單,為了示例簡單,這里客戶端是以同步的方式訪問的。

    static void Main(string[] args)
    {
        var client = new WcfClient.Service.Service1Client();
        using (var file = File.OpenRead(@"R:\client\1.jpg"))
        {
            client.UploadFile(file);
        }
        Console.WriteLine("finished");
    }

四. 擴展功能

前面的例子作為文件上傳還是不夠的,主要存在如下兩個缺點:

  1. 不能指定文件名
  2. 不能獲取上傳進度

首先來解決文件名問題,不能指定文件名的主要原因就是消息體中對參數個數有限制,因此,必須把文件名和stream放在一起作為參數傳入。這個只需要用MessageContract定義一個消息即可:

    [MessageContract]
    public class UploadStreamMessage
    {
        [MessageHeader]
        public string Name { get; set; }
        [MessageBodyMember]
        public Stream Stream { get; set; }
    }

這樣,把UploadStreamMessage作為參數,就可以攜帶文件名信息了。

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        Task UploadFile(UploadStreamMessage msg);
    }

    public class Service1 : IService1
    {
        public async Task UploadFile(UploadStreamMessage msg)
        {
            using (msg.Stream)
            using (var file = File.Create(@"R:\server\" + msg.Name))
            {
                await msg.Stream.CopyToAsync(file);
            }
        }
    }

重新啟動服務后,更新客戶端,此時就會發現生成的代碼中都把參數給分離出來了,非常貼心。

  

    static void Main(string[] args)
    {
        var client = new WcfClient.Service.Service1Client();
        using (var file = File.OpenRead(@"R:\client\1.jpg"))
        {
            client.UploadFile("test.jpg", file);
        }
        Console.WriteLine("finished");
    }

至於當前上傳了多少數據,直接取一下FileStream的Position就可以了,就不用遠程服務器提供接口了。

PS:關於大數據傳輸,流模式並非唯一選擇,我這里也只是對流模式進行了蜻蜓點水般的介紹,更多信息可以參看MSDN的相關文章:1. 大型數據和流,2. 如何啟用流處理。CodeProject上的文章WCF Streaming: Upload/Download Files Over HTTP介紹得非常詳細,強烈推薦一下。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM