.net 流氓
天下无难事,有志者成之;天下无易事,有恒者得之。

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方法从流中读取字节,如下示例注释:

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.IO;
06   
07 namespace UseStream
08 {
09     class Program
10     {
11         //示例如何从流中读取字节流
12         static void Main(string[] args)
13         {
14             var bytes = new byte[] {(byte)1,(byte)2,(byte)3,(byte)4,(byte)5,(byte)6,(byte)7,(byte)8};
15             using (var memStream = new MemoryStream(bytes))
16             {
17                 int offset = 0;
18                 int readOnce = 4;
19                   
20                 do
21                 {
22                     byte[] byteTemp = new byte[readOnce];
23                     // 使用Read方法从流中读取字节
24                     //第一个参数byte[]存储从流中读出的内容
25                     //第二个参数为存储到byte[]数组的开始索引,
26                     //第三个int参数为一次最多读取的字节数
27                     //返回值是此次读取到的字节数,此值小于等于第三个参数
28                     int readCn = memStream.Read(byteTemp, 0, readOnce);
29                     for (int i = 0; i < readCn; i++)
30                     {
31                         Console.WriteLine(byteTemp[i].ToString());
32                     }
33                       
34                     offset += readCn;
35   
36                     //当实际读取到的字节数小于设定的读取数时表示到流的末尾了
37                     if (readCn < readOnce) break;
38                 } while (true);
39             }
40   
41             Console.Read();
42         }
43     }
44 }

2. 使用Stream.BeginRead方法读取FileStream的流内容

注意:BeginRead在一些流中的实现和Read完全相同,比如MemoryStream;而在FileStream和NetwordStream中BeginRead就是实实在在的异步操作了。

如下示例代码和注释:

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.IO;
06 using System.Threading;
07   
08 namespace UseBeginRead
09 {
10     class Program
11     {
12         //定义异步读取状态类
13         class AsyncState
14         {
15             public FileStream FS { get; set; }
16   
17             public byte[] Buffer { get; set; }
18   
19             public ManualResetEvent EvtHandle { get; set; }
20         }
21   
22         static  int bufferSize = 512;
23   
24         static void Main(string[] args)
25         {
26             string filePath = "d:\\test.txt";
27             //以只读方式打开文件流
28             using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
29             {
30                 var buffer = new byte[bufferSize];
31   
32                 //构造BeginRead需要传递的状态
33                 var asyncState = new AsyncState { FS = fileStream, Buffer = buffer ,EvtHandle = new ManualResetEvent(false)};
34   
35                 //异步读取
36                 IAsyncResult asyncResult = fileStream.BeginRead(buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);
37   
38                 //阻塞当前线程直到读取完毕发出信号
39                 asyncState.EvtHandle.WaitOne();
40                 Console.WriteLine();
41                 Console.WriteLine("read complete");
42                 Console.Read();
43             }
44         }
45   
46         //异步读取回调处理方法
47         public static void AsyncReadCallback(IAsyncResult asyncResult)
48         {
49             var asyncState = (AsyncState)asyncResult.AsyncState;
50             int readCn = asyncState.FS.EndRead(asyncResult);
51             //判断是否读到内容
52             if (readCn > 0)
53             {
54                 byte[] buffer;
55                 if (readCn == bufferSize) buffer = asyncState.Buffer;
56                 else
57                 {
58                     buffer = new byte[readCn];
59                     Array.Copy(asyncState.Buffer, 0, buffer, 0, readCn);
60                 }
61   
62                 //输出读取内容值
63                 string readContent = Encoding.UTF8.GetString(buffer);
64                   
65                 Console.Write(readContent);
66             }
67   
68             if (readCn < bufferSize)
69             {
70                 asyncState.EvtHandle.Set();
71             }
72             else {
73                 Array.Clear(asyncState.Buffer, 0, bufferSize);
74                 //再次执行异步读取操作
75                 asyncState.FS.BeginRead(asyncState.Buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);
76             }
77         }
78     }
79 }

3. 使用Stream.Write方法向流中写字节数组

在使用Write方法时,需要先使用Stream的CanWrite方法判断流是否可写,如下示例定义了一个MemoryStream对象,然后向内存流中写入一个字节数组

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.IO;
06   
07 namespace UseStreamWrite
08 {
09     class Program
10     {
11         static void Main(string[] args)
12         {
13             using (var ms = new MemoryStream())
14             {
15                 int count = 20;
16                 var buffer = new byte[count];
17                 for (int i = 0; i < count; i++)
18                 {
19                     buffer[i] = (byte)i;
20                 }
21   
22                 //将流当前位置设置到流的起点
23                 ms.Seek(0, SeekOrigin.Begin);
24   
25                 Console.WriteLine("ms position is " + ms.Position);
26   
27                 //注意在调用Stream的Write方法之前要用CanWrite判断Stream是否可写
28                 if (ms.CanWrite)
29                 {
30                     ms.Write(buffer, 0, count);
31                 }
32   
33                 //正确写入的话,流的位置会移动到写入开始位置加上写入的字节数
34                 Console.WriteLine("ms position is " + ms.Position);
35   
36             }
37   
38             Console.Read();
39         }
40     }
41

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();
            }
        }
    }
}
posted on 2011-08-04 19:39  .net 流氓  阅读(682)  评论(0编辑  收藏  举报