转自 :http://www.cnblogs.com/yukaizhao/archive/2011/07/28/stream.html
Stream在msdn的定义:提供字节序列的一般性视图(provides a generic view of a sequence of bytes)。这个解释太抽象了,不容易理解;从stream的字面意思“河,水流”更容易理解些,stream是一个抽象类,它定义了类似“水流”的事物的一些统一行为,包括这个“水流”是否可以抽水出来(读取流内容);是否可以往这个“水流”中注水(向流中写入内容);以及这个“水流”有多长;如何关闭“水流”,如何向“水流”中注水,如何从“水流”中抽水等“水流”共有的行为。
常用的Stream的子类有:
1) MemoryStream 存储在内存中的字节流
2) FileStream 存储在文件系统的字节流
3) NetworkStream 通过网络设备读写的字节流
4) BufferedStream 为其他流提供缓冲的流
Stream提供了读写流的方法是以字节的形式从流中读取内容。而我们经常会用到从字节流中读取文本或者写入文本,微软提供了StreamReader和StreamWriter类帮我们实现在流上读写字符串的功能。
下面看下如何操作Stream,即如何从流中读取字节序列,如何向流中写字节
1. 使用Stream.Read方法从流中读取字节,如下示例注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace UseStream { class Program { //示例如何从流中读取字节流 static void Main( string [] args) { var bytes = new byte [] {( byte )1,( byte )2,( byte )3,( byte )4,( byte )5,( byte )6,( byte )7,( byte )8}; using (var memStream = new MemoryStream(bytes)) { int offset = 0; int readOnce = 4; do { byte [] byteTemp = new byte [readOnce]; // 使用Read方法从流中读取字节 //第一个参数byte[]存储从流中读出的内容 //第二个参数为存储到byte[]数组的开始索引, //第三个int参数为一次最多读取的字节数 //返回值是此次读取到的字节数,此值小于等于第三个参数 int readCn = memStream.Read(byteTemp, 0, readOnce); for ( int i = 0; i < readCn; i++) { Console.WriteLine(byteTemp[i].ToString()); } offset += readCn; //当实际读取到的字节数小于设定的读取数时表示到流的末尾了 if (readCn < readOnce) break ; } while ( true ); } Console.Read(); } } } |
2. 使用Stream.BeginRead方法读取FileStream的流内容
注意:BeginRead在一些流中的实现和Read完全相同,比如MemoryStream;而在FileStream和NetwordStream中BeginRead就是实实在在的异步操作了。
如下示例代码和注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Threading; namespace UseBeginRead { class Program { //定义异步读取状态类 class AsyncState { public FileStream FS { get ; set ; } public byte [] Buffer { get ; set ; } public ManualResetEvent EvtHandle { get ; set ; } } static int bufferSize = 512; static void Main( string [] args) { string filePath = "d:\\test.txt" ; //以只读方式打开文件流 using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { var buffer = new byte [bufferSize]; //构造BeginRead需要传递的状态 var asyncState = new AsyncState { FS = fileStream, Buffer = buffer ,EvtHandle = new ManualResetEvent( false )}; //异步读取 IAsyncResult asyncResult = fileStream.BeginRead(buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState); //阻塞当前线程直到读取完毕发出信号 asyncState.EvtHandle.WaitOne(); Console.WriteLine(); Console.WriteLine( "read complete" ); Console.Read(); } } //异步读取回调处理方法 public static void AsyncReadCallback(IAsyncResult asyncResult) { var asyncState = (AsyncState)asyncResult.AsyncState; int readCn = asyncState.FS.EndRead(asyncResult); //判断是否读到内容 if (readCn > 0) { byte [] buffer; if (readCn == bufferSize) buffer = asyncState.Buffer; else { buffer = new byte [readCn]; Array.Copy(asyncState.Buffer, 0, buffer, 0, readCn); } //输出读取内容值 string readContent = Encoding.UTF8.GetString(buffer); Console.Write(readContent); } if (readCn < bufferSize) { asyncState.EvtHandle.Set(); } else { Array.Clear(asyncState.Buffer, 0, bufferSize); //再次执行异步读取操作 asyncState.FS.BeginRead(asyncState.Buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState); } } } } |
3. 使用Stream.Write方法向流中写字节数组
在使用Write方法时,需要先使用Stream的CanWrite方法判断流是否可写,如下示例定义了一个MemoryStream对象,然后向内存流中写入一个字节数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace UseStreamWrite { class Program { static void Main( string [] args) { using (var ms = new MemoryStream()) { int count = 20; var buffer = new byte [count]; for ( int i = 0; i < count; i++) { buffer[i] = ( byte )i; } //将流当前位置设置到流的起点 ms.Seek(0, SeekOrigin.Begin); Console.WriteLine( "ms position is " + ms.Position); //注意在调用Stream的Write方法之前要用CanWrite判断Stream是否可写 if (ms.CanWrite) { ms.Write(buffer, 0, count); } //正确写入的话,流的位置会移动到写入开始位置加上写入的字节数 Console.WriteLine( "ms position is " + ms.Position); } Console.Read(); } } } |
4. 使用Stream.BeginWrite方法异步写;异步写可以提高程序性能,这是因为磁盘或者网络IO的速度远小于cpu的速度,异步写可以减少cpu的等待时间。
如下使用FileStream异步写文件的操作示例
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Threading; namespace UseStreamBeginWrite { class Program { /// <summary> /// 异步回调需要的参数封装类 /// </summary> class AsyncState { public int WriteCountOnce { get ; set ; } public int Offset { get ; set ; } public byte [] Buffer { get ; set ; } public ManualResetEvent WaitHandle { get ; set ; } public FileStream FS { get ; set ; } } static void Main( string [] args) { //准备一个1K的字节数组 byte [] toWriteBytes = new byte [1 << 10]; for ( int i = 0; i < toWriteBytes.Length; i++) { toWriteBytes[i] = ( byte )(i % byte .MaxValue); } string filePath = "d:\\test.txt" ; //FileStream实例 using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) { int offset = 0; //每次写入32字节 int writeCountOnce = 1 << 5; //构造回调函数需要的状态 AsyncState state = new AsyncState{ WriteCountOnce = writeCountOnce, Offset = offset, Buffer = toWriteBytes, WaitHandle = new ManualResetEvent( false ), FS = fileStream }; //做异步写操作 fileStream.BeginWrite(toWriteBytes, offset, writeCountOnce, WriteCallback, state); //等待写完毕或者出错发出的继续信号 state.WaitHandle.WaitOne(); } Console.WriteLine( "Done" ); Console.Read(); } /// <summary> /// 异步写的回调函数 /// </summary> /// <param name="asyncResult">写状态</param> static void WriteCallback(IAsyncResult asyncResult) { AsyncState state = (AsyncState)asyncResult.AsyncState; try { state.FS.EndWrite(asyncResult); } catch (Exception ex) { Console.WriteLine( "EndWrite Error:" + ex.Message); state.WaitHandle.Set(); return ; } Console.WriteLine( "write to " + state.FS.Position); //判断是否写完,未写完继续异步写 if (state.Offset + state.WriteCountOnce < state.Buffer.Length) { state.Offset += state.WriteCountOnce; Console.WriteLine( "call BeginWrite again" ); state.FS.BeginWrite(state.Buffer, state.Offset, state.WriteCountOnce, WriteCallback, state); } else { //写完发出完成信号 state.WaitHandle.Set(); } } } } |