.net Stream篇(六)

BufferedStreamhtml

目錄:web

 

 

 

 

1 簡單介紹一下BufferedStream數組

在前幾章的講述中,咱們已經可以掌握流的基本特性和特色,通常進行對流的處理時系統肩負着IO所帶來的開銷,調用十分頻繁緩存

這時候就應該想個辦法去減小這種開銷,並且必須在已有Stream進行擴展,有了以上2點需求,那麼咱們今天的主題,服務器

BufferedStream閃亮登場了,BufferedStream可以實現流的緩存,換句話說也就是在內存中可以緩存必定的數據而不是app

時時給系統帶來負擔,同時BufferedStream能夠對緩存中的數據進行寫入或是讀取,因此對流的性能帶來必定的提高socket

可是沒法同時進行讀取或寫入工做,若是不使用緩衝區也行,BufferedStream可以保證不用緩衝區時不會下降因緩衝區帶來ide

的讀取或寫入性能的降低函數

 

2 如何理解緩衝區性能

緩衝區是內存中的一塊連續區域,用來緩存或臨時存儲數據,也就是說流能夠經過緩衝區逐步對數據進行讀取或寫入操做,

BufferedStream 中的緩存區能夠由用戶設定,其表現形式爲byte數組,想象下沒有緩存區將是很可怕的,假如咱們的

非固態硬盤沒有緩衝區,若是咱們下載速度達到驚人的10m左右,那麼下載一個2G或更大的文件時,磁頭的讀寫是很是

的頻繁,直接的結果是磁頭壽命急劇減小,甚至將硬盤直接燒燬或者損壞

 

3 BufferedStream的優點

理解了緩衝區的重要性後,讓咱們在來談下BufferedStream的優點,首先你們確定覺的疑惑爲何MemoryStream 一樣

也是在內存中對流進行操做,和BufferedStream有什麼區別呢?BufferedStream並非將全部內容都存放到內存中

而MemoryStream則是BufferedStream必須跟其餘流如FileStream結合使用,而MemoryStream則不用,聰明的你

確定可以想到,BufferedStream必然相似於一個流的包裝類,對流進行」緩存功能的擴展包裝」,因此BufferedStream的

優點不只體如今其原有的緩存功能上,更體如今如何幫助原有類實現其功能的擴展層面上

 

4 從BufferedStream 中簡單學習下裝飾模式

如何理解裝飾模式

             咱們在作項目時或者設計項目時經常會碰到這個問題 :咱們該如何擴展已有的類功能或者若是擴展一系列派生類的

             功能呢,可能你馬上會想到繼承,的確不錯,可是若是你仔細看下圖而且展開必定的想象的話,你就會發現繼承可能

             致使子類的膨脹性增長,以下圖所示

           

首先仍是得注意如下原則

1. 多用組合,少用繼承

利用繼承設計子類的行爲,是在編譯時靜態決定的,並且全部的子類都會繼承到相同的行爲。然而,若是可以利用組合的作法擴展對象的行爲,就能夠在運行時動態地進行擴展。

2. 類應設計的對擴展開放,對修改關閉。

那麼咱們該如何避免子類的擴張同時又實現Girl類原有類或派生類的新功能呢?

首先咱們要達到2個目的:

1 可以爲Girl的全部派生類都實現新功能(不修改派生類的結構)

2 利用對象組合的方式

 

爲了知足爲Girl 類全部派生類都能使用,那麼咱們就加上一個Girl的裝飾類GirlWrapper:

複製代碼
  public abstract class GirlWrapper : Girl
    {
        protected Girl girl;

        public GirlWrapper(Girl thisGril)
        {
            this.girl = thisGril;
        }
        public override void Decrorator()
        {
            girl.Decrorator();
        }
        public override string ToString()
        {
            return string.Format("{0}:{1}", this.girl.GirlName, this.girl.Nation);
        }
    }
複製代碼

該類繼承了Girl類,從而保證了和其餘派生類有共同的基本結構,

既然有了這個裝飾類,那咱們即可以刪掉原來的Singing 接口,添加一個

SingingGirlWrapper類來實現對girl的包裝類,

複製代碼
    public class SingingGirlWrapper : GirlWrapper
    {
        public SingingGirlWrapper(Girl thisGril)
            : base(thisGril)
        {

        }
        public void Decorator() 
        {
            Console.WriteLine("SingingGirlWrapper decorateor:The girl named {0} who from {1} is {2} can singing nao", 
                this.GirlName, this.Nation, this.girl.GetType().Name);
            base.Decrorator();
        }

        public override string ToString()
        {
            return base.ToString();
        }
    }
複製代碼

           你們沒必要拘泥於派生的包裝類,你徹底能夠創建一個新的girl包裝類來實現特定的功能,上述例子只是演示下派生的包裝類
          這樣的話,咱們便使用了組合的方式實現了既保留原有的接口(或者抽象類),又動態添加了新功能

          

在使用時咱們能夠將派生類的對象放入裝飾類的構造中,這樣的話,在執行包裝類Decorator方法時,能夠執行被包裝對象的

Decorator方法和包裝類的Decorator方法從而實現對Girl派生類的包裝,這樣的話就能實現靈活的組合擴展。

複製代碼
static void Main(string[] args)
        {
            Queen queen = new Queen("Mary","Unite States");
            SingingGirlWrapper sgw = new SingingGirlWrapper(queen);
            sgw.Decorator();
            Console.ReadLine();
        }
複製代碼


              再次理解下裝飾模式在Stream中的做用

經過以上的例子在回到BufferStream章節中,你們確定一眼就看出了BufferStream其實就是上述例子中的wrapper類

而Stream 類就是其共同的父類,爲了給全部的流類提供緩衝功能因此BufferedStream便誕生了,這樣的話,咱們能夠

不用修改其派生類結構,便能靈活組合將緩衝功能嵌入stream中

 

5 BufferedStream的構造

BufferedStream(Stream)

其實BufferedStream的構造主要功能仍是設置緩衝區大小,若是沒有指定則默認是用4096字節的進行初始化

BufferedStream(Stream, Int32)

第二個參數是手動指定緩衝區大小

第一次使用此構造函數初始化 BufferedStream 對象時分配共享讀/寫緩衝區。 若是全部的讀和寫都大於或等於緩衝區大小,則不使用共享緩衝區

 

6 BufferedStream的屬性

*1 CanRead 已重寫。獲取一個值,該值指示當前流是否支持讀取。 
若是流支持讀取,則爲 true;若是流已關閉或是經過只寫訪問方式打開的,則爲 false。 
若是從 Stream 派生的類不支持讀取,則對 StreamReader、StringReader、TextReader 的 Read、ReadByte、BeginRead、EndRead 和 Peek 方法的調用將引起 NotSupportedException。 
若是該流已關閉,此屬性將返回 false。

 

*2 CanSeek 已重寫。獲取一個值,該值指示當前流是否支持查找。 
若是流支持查找,則爲 true;若是流已關閉或者若是流是由操做系統句柄(如管道或到控制檯的輸出)構造的,則爲 false。 
若是從 Stream 派生的類不支持查找,則對 Length、SetLength、Position 和 Seek 的調用將引起 NotSupportedException。 
若是該流已關閉,此屬性將返回 false。

 

*3  CanWrite 已重寫。獲取一個值,該值指示當前流是否支持寫入。 
若是流支持寫入,則爲 true;若是流已關閉或是經過只讀訪問方式打開的,則爲 false。 若是從 Stream 派生的類不支持寫入,

則調用 SetLength、Write 或 WriteByte 將引起 NotSupportedException。 若是該流已關閉,此屬性將返回 false。

 

*4  Length 已重寫。獲取流長度,長度以字節爲單位。

 

*5  Position 已重寫。獲取當前流內的位置。

 get 訪問器調用 Seek 獲取基礎流中的當前位置,而後根據緩衝區中的當前位置調整此值

 set 訪問器將之前寫入緩衝區的全部數據都複製到基礎流中,而後調用 Seek

 支持搜索到超出流長度的任何位置。

 

7 BufferedStream的方法

BufferStream的方法基本上和Stream類一致,沒有其獨特的方法

關於以上方法的注意事項的你們也可參考個人第一篇

 

 簡單示例:利用socket 讀取網頁並保存在本地

複製代碼
    class Program
    {
        static void Main(string[] args)
        {
            Server server = new Server("http://www.163.com/");
            server.FetchWebPageData();
        }
    }

    public class Server
    {
        //端口
        const int webPort = 80;
        //默認接收緩存大小
        byte[] receiveBufferBytes = new byte[4096];
        //須要獲取網頁的url
        private  string webPageURL;
        public Server(string webPageUrl)
        {
            webPageURL = webPageUrl;
        }

       /// <summary>
        ///  從該網頁上獲取數據
       /// </summary>
        public void FetchWebPageData() 
        {
            if (!string.IsNullOrEmpty(webPageURL))
            FetchWebPageData(webPageURL);
            Console.ReadLine();
        }

        /// <summary>
        /// 從該網頁上獲取數據
        /// </summary>
        /// <param name="webPageURL">網頁url</param>
        private void FetchWebPageData(string webPageURL) 
        {
            //經過url獲取主機信息
            IPHostEntry iphe = Dns.GetHostEntry(GetHostNameBystrUrl(webPageURL));
            Console.WriteLine("遠程服務器名: {0}", iphe.HostName);
            //經過主機信息獲取其IP
            IPAddress[] address = iphe.AddressList;
            IPEndPoint ipep = new IPEndPoint(address[0], 80);
            //實例化一個socket用於接收網頁數據
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //鏈接
            socket.Connect(ipep);
            if (socket.Connected)
            {
                //發送頭文件,這樣才能下載網頁數據
                socket.Send( Encoding.ASCII.GetBytes( this.GetHeader(webPageURL)));
            }
            else { return; }
            //接收頭一批數據
            var count = socket.Receive(receiveBufferBytes);
            //轉化成string 
            var getString = Encoding.Default.GetString(receiveBufferBytes);
           //建立文件流
            FileStream fs = new FileStream(@"d:\\Test.html", FileMode.OpenOrCreate);
            //建立緩存流
            BufferedStream bs = new BufferedStream(fs);
            using (fs)
            {
                using (bs)
                {
                    byte[] finalContent = Encoding.Default.GetBytes(getString.ToCharArray());
                    //將頭一批數據寫入本地硬盤
                    bs.Write(finalContent, 0, finalContent.Length);
                    //循環經過socket接收數據
                    while (count > 0)
                    {
                        count = socket.Receive(receiveBufferBytes, receiveBufferBytes.Length, SocketFlags.None);
                        //直接將獲取到的byte數據寫入本地硬盤
                        bs.Write(receiveBufferBytes, 0, receiveBufferBytes.Length);
                        Console.WriteLine(Encoding.Default.GetString(receiveBufferBytes));
                    }
                    bs.Flush();
                    fs.Flush();
                    bs.Close();
                    fs.Close();
                }
            }
        }
        /// <summary>
        /// 獲得header
        /// </summary>
        /// <param name="url">網頁url</param>
        /// <returns>header字符串</returns>
        private string GetHeader(string webPageurl) 
        {
            return "GET " + GetRelativeUrlBystrUrl(webPageurl) + " HTTP/1.1\r\nHost: "
                + GetHostNameBystrUrl(webPageurl) + "\r\nConnection: Close\r\n\r\n";
        }

        /// <summary>
        /// 獲得相對路徑
        /// </summary>
        /// <param name="strUrl">網頁url</param>
        /// <returns></returns>
        private string GetRelativeUrlBystrUrl(string strUrl)
        {
            int iIndex = strUrl.IndexOf(@"//");
            if (iIndex <= 0)
                return "/";
            string strTemp = strUrl.Substring(iIndex + 2);
            iIndex = strTemp.IndexOf(@"/");
            if (iIndex > 0)
                return strTemp.Substring(iIndex);
            else
                return "/";
        }
        /// <summary>
        /// 根據Url獲得host
        /// </summary>
        /// <param name="strUrl">網頁url</param>
        /// <returns></returns>
        private string GetHostNameBystrUrl(string strUrl)
        {
            int iIndex = strUrl.IndexOf(@"//");
            if (iIndex <= 0)
                return "";
            string strTemp = strUrl.Substring(iIndex + 2);
            iIndex = strTemp.IndexOf(@"/");
            if (iIndex > 0)
                return strTemp.Substring(0, iIndex);
            else
                return strTemp;
        }

    }
複製代碼
相關文章
相關標籤/搜索