【Stream—6】BufferedStream相关知识分享
一、简单介绍以下BufferedStream
在前几章的讲述中,我们已经能够掌握流的基本特性和特点,一般进行对流的处理时,系统肩负着IO所带来的开销,调用十分频繁,这时候就应该想个办法减少这种开销,而且必须在已有的Stream进行扩展,有了以上2点要求,那么我们今天的主题BufferedStream闪亮登场了。BufferedStream能够实现流的缓存,换句话说也就是在内存中能够缓存一定的数据而不是时时给系统带来负担,同时BufferedStream可以对缓存中的数据进行写入或读取,所以对流的性能带来一定的提升,但是无法同时进行读取或写入工作,如果不使用缓冲区也行,BufferedStream能够保证不用缓冲区时不会降低因缓冲区带来的读取或写入性能的下降
二、如何理解缓冲区
缓冲区是内存中的一块连续区域,用来缓存或临时存储数据,也就是说可以通过缓冲区逐步对数据进行读取或写入操作。BufferedStream中的缓冲区可以由用户设定,其表现形式为Byte数组,想象下没有缓冲区将是多么的可怕,假如我们的非固态硬盘没有缓冲区,如果我们下载速度达到惊人的10m左右,那么下载一个2G或者更大的文件时,磁头的读写是非常的频繁,直接的结果是磁头寿命急剧减少,甚至将硬盘直接烧毁或损坏。
三、BufferedStream的优势
理解了缓冲区的重要性后,让我们再来谈一下BufferedStream的优势,首先大家肯定觉得疑惑,为什么MemoryStream同样也是在内存中对流进行操作,和BufferedStream有什么区别呢?BufferedStream并不是将所有内容都存放到内存中,而MemoryStream则是,BufferedStream必须跟其他流如FileStream结合使用,而MemoryStream则不需要,聪明的你肯定能够想到,BufferedStream必然类似于一个流的包装类,对流进行“缓存功能的扩展包装”,所以BufferedStream的优势不仅体现在其原有的缓存功能上,更体现在如何帮助原有的类实现其功能上的扩展层面上。
四、BufferedStream的构造
1、BufferedStream(Stream stream)
其实BufferedStream的构造主要功能还是设置缓冲区大小,如果没有制定则默认是用4096字节的进行初始化
2、BufferedStream(Stream stream, int i)
第二个参数是手动制定缓冲区大小,第一次使用此构造函数初始化BufferedStream对象时分配共享读/写缓冲区。如果所有的读和写大小都等于缓冲区大小,则不使用共享缓冲区、
五、BufferedStream的属性
1、CanRead已重写。
获取一个值,该值指示当前流是否支持读取。如果支持读取,则为true;如果流已关闭或者时通过只读访问方式打开的,则为false;如果Stream派生的类不支持读取,则对StreamRerader、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。支持搜索到超出流长度的任何位置。
六、BufferedStream的方法
BufferedStream的方法基本上和Stream类一致,没有其独有的方法
七、简单示例:李用socket读取网页并保存在本地
1 public class Server
2 {
3 //端口
4 private const int webPort = 80;
5 //默认接收缓存大小
6 private byte[] receiveBufferBytes = new byte[4096];
7 //需要获取网页的url
8 private readonly string _webPageUrl;
9
10 public Server(string webPageUrl)
11 {
12 _webPageUrl = webPageUrl;
13 }
14
15 /// <summary>
16 /// 从网页上获取数据
17 /// </summary>
18 public void FetchWebPageData()
19 {
20 if (!string.IsNullOrWhiteSpace(_webPageUrl))
21 {
22 FetchWebPageData(_webPageUrl);
23 }
24
25 Console.ReadLine();
26 }
27
28 /// <summary>
29 /// 从网页上获取数据
30 /// </summary>
31 /// <param name="webPageUrl">网页Url</param>
32 private void FetchWebPageData(string webPageUrl)
33 {
34 //通过Url获取主机信息
35 IPHostEntry iphe = Dns.GetHostEntry(GetHostNameByStrUrl(webPageUrl));
36 Console.WriteLine($"远程服务器名:{iphe.HostName}");
37 //通过主机信息获取其IP
38 IPAddress[] address = iphe.AddressList;
39 IPEndPoint ipep = new IPEndPoint(address[0], 80);
40 //实例化一个socket用于接收网页数据
41 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
42 //连接
43 socket.Connect(ipep);
44 if (socket.Connected)
45 {
46 //发送头文件,这样次啊能下载网页数据
47 socket.Send(Encoding.ASCII.GetBytes(GetHeader(webPageUrl)));
48 }
49 else
50 {
51 return;
52 }
53 //接收头一批数据
54 var count = socket.Receive(receiveBufferBytes);
55 //转化为string
56 var getString = Encoding.Default.GetString(receiveBufferBytes);
57 //创建流文件
58 FileStream fs=new FileStream(@"f:\\test.html",FileMode.OpenOrCreate);
59 //创建缓存流
60 BufferedStream bs=new BufferedStream(fs);
61 using (fs)
62 {
63 using (bs)
64 {
65 byte[] finalContent = Encoding.Default.GetBytes(getString.ToCharArray());
66 //将头一批数据写入本地硬盘
67 bs.Write(finalContent,0,finalContent.Length);
68 //循环通过socket接收数据
69 while (count > 0)
70 {
71 count = socket.Receive(receiveBufferBytes, receiveBufferBytes.Length, SocketFlags.None);
72 //直接将接收到的byte数据写入本地硬盘
73 bs.Write(receiveBufferBytes, 0, receiveBufferBytes.Length);
74 Console.WriteLine(Encoding.Default.GetString(receiveBufferBytes));
75 }
76 }
77 }
78 }
79
80 /// <summary>
81 /// 得到Header
82 /// </summary>
83 /// <param name="webPageUrl"></param>
84 /// <returns></returns>
85 private string GetHeader(string webPageUrl)
86 {
87 return $"GET {GetRelativeUrlByStrUrl(webPageUrl)}HTTP/1.1\r\nHOST:{GetHostNameByStrUrl(webPageUrl)}\r\nConnection:Close\r\n\r\n";
88 }
89
90 /// <summary>
91 /// 得到相对路径
92 /// </summary>
93 /// <param name="strUrl">网页url</param>
94 /// <returns></returns>
95 public string GetRelativeUrlByStrUrl(string strUrl)
96 {
97 var iIndex = strUrl.IndexOf(@"//", StringComparison.Ordinal);
98 if (iIndex <= 0)
99 {
100 return "/";
101 }
102
103 var strTemp = strUrl.Substring(iIndex + 2);
104 iIndex = strTemp.IndexOf(@"/", StringComparison.Ordinal);
105 if (iIndex > 0)
106 {
107 return strTemp.Substring(iIndex);
108 }
109
110 return "/";
111 }
112
113 /// <summary>
114 /// 根据Url得到Host
115 /// </summary>
116 /// <param name="strUrl">网页Url</param>
117 /// <returns></returns>
118 public string GetHostNameByStrUrl(string strUrl)
119 {
120 var iIndex = strUrl.IndexOf(@"//", StringComparison.Ordinal);
121 if (iIndex <= 0)
122 {
123 return "";
124 }
125
126 var strTemp = strUrl.Substring(iIndex + 2);
127 iIndex = strTemp.IndexOf(@"/", StringComparison.Ordinal);
128 if (iIndex > 0)
129 {
130 return strTemp.Substring(0, iIndex);
131 }
132
133 return strTemp;
134 }
135 }
好了,BufferedStream相关知识就分享到这里了。