C#网络编程 (二) 数据流的类型和应用

C#网络编程 (二) 数据流的类型和应用

流(stream)是串行化设备的抽象表示,可以是文件,内存,网络套接字等。
Stream类是所有流类的抽象基类。
—————————————————————————————————————————————
数据流是什么?

按照定义,数据流是是对串行传输数据的一种抽象表示,是对I/O的一种抽象。
我们可以通过将文件夹数据、代码生成结果、设备、内存等各种形式的数据用流的方式进行传输。
从程序的角度出发,如果将数据从来源取出,可以用StreamReader把数据储存在内存缓冲区;
如果将数据写入目的地,可以使用StreamWriter把内存缓冲区的数据写入目标地址。
数据流的操作包括写、读,查找,其写入和读取操作都是基于字节的。
Stream是一种虚拟类,是所有数据流的基类,无法创建实例。

—————————————————————————————————————————————
流的概念

在.NET中Stream
是所有流的抽象基类。流是字节序列的抽象概念,或者说是计算机在处理文件或数据时产生的二进制序列。例如文件、输入/输出设备、内部进程通信管道或者 TCP/IP 套接字
Stream 类及其派生类提供这些不同类型的输入和输出的一般视图,使程序员不必了解操作系统和基础设备的具体细节。简单的说流提供了不同介质之间的数据交互功能。
在.NET中常用的流有BufferedStream 、FileStream、MemoryStream 和NetworkStream,他们都是位于System.IO和System.Net命名空间下。
流涉及三个基本操作:
读取,写入和查找。根据基础数据源或储存库,流可能只支持这些功能中的一部分。
有些流实现执行基础数据的本地缓冲以提高性能。对于这样的流,Flush 方法可用于清除所有内部缓冲区并确保将所有数据写入基础数据源或储存库。
—————————————————————————————————————————————
在VS.NET平台上,包括了下面三种数据流类型

类型                              	命名空间	                        功能
网络流  NetworkStream	 System.Net.Sockets	              网络数据的读写操作
内存流  MemoryStream	 System.IO	                      内存数据的处理和转换
文件流 FileStream	     System.IO	                      文件的读写操作

NetworkStream类
初始化:


初始化一个套接字
Socket netSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//由套接字生成一个网络流
NetworkStream netStream = new NetworkStream(netSocket);
  ServerReader = new StreamReader(NetStream);     //读数据
  ServerWriter = new StreamWriter(NetStream);     //写数据 

常用的方法和属性有:

主要属性和方法 描述
DataAvailable 有数据可读时,其值为真

Read()、Write()	        从/向NetworkStream中读取/写入数据
ReadByte()、WiriteByte()	从/向NetworkStream读取/写入一个字节的数据
EndRead()、BeginRead()	结束/启动一个异步NetworkStream的读操作
BeginWrite()、EndWrite()	开始/启动一个异步NetworkStream的写操作
Flush()	刷新流
Close()	关闭对象

MemoryStream类

这个类所创建的流以内存而不是磁盘或网络连接作为支持存储区。

在屏幕捕获、音频实时处理等数据量大的场合得到应用,用于读写内存数据流,经常作为不同缓冲数据之间的转换方式

  MemoryStream myStream = new MemoryStream();
  //将待写入数据从字符串转换为字节数组
  UnicodeEncoding encoder = new UnicodeEncoding();
   byte[] bytes = encoder.GetBytes("魑魅魍魉);
 //向内存流中写入数据
 for (int i = 0; i < 10; i++)
 {
      Console.WriteLine("第{0}写入新数据", i);
 }

FileStream类
—————————————————————————————————————————————
对于文件的读写,实际是把硬盘中的数据读入内存和把内存的数据写入硬盘,他们数据之间的交换就是通过流来完成的。在.NET中这个功能是由FileStream类完成的。他提供的Write和Read方法可以对文件进行读写操作。
—————————————————————————————————————————————
文件流用于对文件的读写,有两类

文本文件的读写 StreamReader StreamWriter
二进制文件的读写 BinaryReader BinaryWriter

System.IO命名空间下,包含了对目录和文件的操作的类

Directory和DirectoryInfo类    文件和目录的操作
File和FileInfo类              对文件的各种操作
Path类                        对包含文件和目录路径信息的字符串进行操作的静态方法

1:FileStream读写文件

使用 FileStream 类对文件系统上的文件进行读取、写入、打开和关闭操作,并对其他与文件相关的操作系统句柄进行操作,如管道、标准输入和标准输出。读写操作可以指定为同步或异步操作。FileStream 对输入输出进行缓冲,从而提高性能。

2:StreamWriter写文件

上面的FileStream操作文件读写,每次都需要使用字节数组,因为FileStream操作对象是字节。而.NET提供了StreamWriter和StreamReader对象来对流进行读写操作。
他的构造函数可以接受一个Stream对象。从而对流进行操作。他们的内部有个一Stream对象来维护传入的各种流对象。并且也提供了Write和Read方法。实际上这2个类是对流读写的的一个包装,方便我们使用。当我们传一个流对象时,调用读写方法是,实际调用该对象自己重写的方法。而当我们在构造函数中传入的是文件路径时,他就成为了对文件读写的操作。因为他在内部构建了一个FileStream对象,并交给内部的Stream对象维护。

 static void Main(string[] args)
 {
   try
   {
      StreamWriter sw = new StreamWriter(@"c:\text.txt");
      sw.Write("This is StreamWriter");
   }
   catch (Exception ex)
   {
      Console.WriteLine(ex.Message);
   }
   finally
   {
      Console.ReadKey();
   }
}

上面的代码是使用StreamWriter对文件进行写操作,当执行到ReadKey时,我们发现文件没有被写入,这个和FileStream是一样的,但是当程序执行完后我们发现,数据还是没有被写入。如果我们写入的数据量比较大时,数据也被写入到文件中,但是会发现写入的数据可能并不完整。因为只有当StreamWriter内部的缓冲区充满或调用Flush时,才会把数据写入Stream对象中。StreamWriter 未将最后 1 至 4 KB 数据写到文件。后面会具体解释。

MSDN中对此的解释是: StreamWriter 在内部缓冲数据,这需要调用 Close 或 Flush 方法将缓冲数据写到基础数据存储区。如果没有适当地调用 Close 或 Flush,StreamWriter 实例中缓冲的数据可能不会按预期写出。
在StreamWriter中也有Flush方法,清理当前编写器的所有缓冲区,并使所有缓冲数据写入基础流。对于StreamWriter来说,也有自己的缓冲区,而不同的是StreamWriter缓冲区是char[]而不是byte[]。而StreamWriter的write方法只是把数据写入到自己的缓冲区中,所以我们必须条用Flush方法来写入到文件中,而Flush方法中则是先调用了FileStream的write方法把StreamWriter缓冲区的数据写入到FileStream的缓冲区中,最后在调用FileStream的Flush方法写入文件。

3: FileStream和StreamWriter的依赖关系
如果我们使用public StreamWriter(string path)构造方法不会存在这个问题,因为FileStrem对象是内部控制的,如果我们用StreamWriter(Stream)构造方法就可能存在一些问题。

 static void Main(string[] args)
        {
            FileStream fs = null;
            StreamWriter sw = null;
            try
            {
                fs = new FileStream(@"c:\text.txt", FileMode.Create);
                sw = new StreamWriter(fs);
                string message = "This is StreamWriter\r\n";
                for (int i = 0; i < 10; i++)
                {
                    message += message;
                }
                sw.Write(message);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                fs.Close();
                sw.Close();
                Console.ReadKey();
            }
        }

3: BinaryWriter

BinaryWriter对象也可以用写文件,以二进制形式将基元类型写入流,并支持用特定的编码写入字符串。与StreamWriter不同的是,他不存在缓冲区丢失的问题。因为他每次调用Write方法以后说首先把数据写入自己的char[]数组,然后转换为指定编码的Byte[]数组,最后调用Stream的Write方法写入到流的缓冲区。
BinaryWriter对象也有Flush方法,但是只是简单的调用了Stream的Flush方法,而他的Close和Dispose方法则是调用了Stream的Close方法。和上面一样
BinaryWriter对象也没有实现Finalize方法,但是因为他没有把数据放到自己的缓冲区,每次都是立即写入到流中。所以即便不调用Flush方法或是显式关闭对象,最后也会全部被写入到文件中,因为数据全部在FileStream的缓冲区中,而程序结束时Finalize方法会调用Flush把数据写入文件。

`
``csharp
一个文件操作流的例子
//文件流读取
FileStream fileStream = File.Open(@“D:\Key.txt”, FileMode.Open);//初始化待读取文件流
byte[] array = new byte[fileStream.Length];//初始化字节数组,用来存储读取到的字节
fileStream.Read(array, 0, array.Length);//读取文件流中数据,写入到字节数组中
fileStream.Close(); //关闭流
string str = Encoding.Default.GetString(array);//将字节数组内容转化为字符串
Console.WriteLine(str);

//写文件流
byte[] array1 = new byte[1024];
array1 = Encoding.UTF8.GetBytes(str);
FileStream fileStream1 = new FileStream(@“D:\Key.txt”, FileMode.OpenOrCreate, FileAccess.Write);
fileStream1.Write(array1, 0, array1.Length);



```csharp
StreamWriter sw = new StreamWriter("MyFile.txt", true, System.Text.Encoding.Unicode);
sw.WriteLine("第一条语句"); 
sw.WriteLine("第二条语句");
sw.Close();
StreamReader sr = new StreamReader("MyFile.txt", System.Text.Encoding.Unicode);
string str1 = sr.ReadLine();
Console.WriteLine(str1);
string str2 = sr.ReadLine();
Console.WriteLine(str2);
sr.Close();
Console.ReadKey();

所以我们自定义一个流继承自Stream 看看哪些属性必须重写或自定义:
数据流

数据流(data stream)是一组有序,有起点和终点的字节的数据序列。
在C#里,数据流提供了一种通用的方式和字节队列进行交互。

数据流的操作

逐字节顺序写入(将数据从内存缓冲传出到外部资源)
逐字节顺序读取(将数据从外部数据资源传输到内存缓冲区)
随机读写(从某个位置开始逐字顺序读写)

分类
根据数据源的类型(C#语言采用这种方式进行分类)

分为文件流(FileStream)
内存流(MemoryStream)
网络流(NetworkStream)
加密流(CryptoStream)

根据读写数据的类型

分为字节流和字符流

根据输入输出方向

输入流和输出流

Stream流,在System.IO命名空间下,是所有数据流的基类,抽象类无法创建实例。

Position:获取或设置流当前的位置

Length:以字节为单位流的长度

CanWriter:当前流是否支持写入

CanTimeout:当前流是否超时

CanSeek:当前流是否支持查找,当CanSeek为false的时候,是不允许使用Position属性和调用Seek函数的,会抛出异常。

CanRead:当前流是否可读

Synckronized:根据当前流创建一个线程安全的的流对象

Close:关闭流并释放相关资源。

FileStream

文件流,在System.IO命名空间下,用于对各种类型的文件进行读写。 必须调用Dispose进行资源释放。
创建FileStream:FileStream构造函数之外,也可以通过File.OpenWrite和File.OpenRead创建FileStream对象

Flush:清除流的缓冲区,将已经缓冲的数据写入文件中。

Lock:锁定当前流,防止其他进程对文件进行读写。

UnLock:解除锁定,允许其他进程对文件进行读写。

SetLength:设置流的长度。

Read:读取一个一个字节块,并写入给定的缓冲区,offset是数组的偏移量,
coun为读取的字节长度。每当读取一个字节后Position会增加1。放返回值为每次读取到的实际字节数。

Seek:设置流的偏移量,offset 偏移量,origin是指定偏移量的参考位置,开始位置、结束位置、当前位置。

MemoryStream

内存流,在System.IO命名空间下,对保存在内存中的字节数组进行操作。
由于内存流的容量可以自动增长,因此在数据加密以及对长度不定的数据进行缓存等场合使用。

Capacity:获取或设置为此流分配的字节数

GetBuffer:获取流中的无符号字节数组,返回的是分配给流空间大小的字节数组
(也就是Capacity大小的字节数组),而不是实际占用了的字节数组。

NetworkStream

网络流,在System.Net.Sockets命名空间下,利用该类可以通过网络发送或接收数据。可以将NetworkStream看作在数据源和接收端之间架设了一个数据通道,读取和写入就可以针对通道来进行。

NetworkStream仅支持面向连接的套接字,也就是采用TCP协议的连接。

创建NetworkStream:构造函数参数为Socket对象或调用TcpClient对象GetStream方法得到NetworkStream

Position:网络流无法访问该属性会抛出异常

CanSeek:始终返回false

Seek:网络流无法调用该函数会抛出异常。

DataAvailable:查看缓冲区(数据缓冲区)中是否有数据等待读出。

Write:将字节数组从进程缓冲区写入到TCP发送缓冲区,Write为同步方法,
会阻塞线程直到发送成功或返回异常位置。

Read:将字节数组从TCP接收缓冲区读出到进程缓冲区,
调用Read之前应先判断DataAvailable属性是否为true

CryptoStream

加密流,在System.Security.Cryptography命名空间下,该类可加密流的方式加密或者解密数据,而且只能用于对称加密。

实现任何CryptoStream的任何被加密对象都可以和实现Stream的任何对象链接起来,因此一个对象的流式处理输出可以馈送到另一个对象的输入,而不需要分别存储中间结果。

Read:将需要解密的流写入到字节数组中,字节数组是已经解密的。

Write:将需要进行加密的流写入到字节数组中,字节数组中的数据是已经被加密的。

FlushFinalBlock:将内部缓冲区的数据进行写入并清空缓冲区。调用Flush无效。

StreamReader

继承自TextReader,在System.IO命名空间下。对文本数据进行读取操作时使用该类。

创建StreamReader:构造函数,参数使用FileStream、NetworkStream、MemoryStream的对象或使用文件路径。

Close:读写完成后必须使用CLose进行流的关闭.

BaseStream:获取对应的底层流,也就是创建StreamWriter或StreamReader时使用的Stream对象。

CurrentEncoding:获取当前流设置的编码格式。

ReadToEnd:从底层流的Position开始读取字符到结束为止

EndOfStream:获取底层流的Position是否为末尾为止。

StreamWriter

继承自TextWriter,在System.IO命名空间下。对文本数据进行写入操作时使用该类。

创建StreamWriter:除构造函数外,调用File.CreateText、FileInfo.CreateText均可以获取StreamWriter对象。

AutoFlush:是否在调用Write函数后自动调用Flush函数将字符缓冲到底层流。

Flush:清除当前写入器的所有缓冲区,并使任何已缓存的数据变写入底层流。

BinaryReader

在System.IO命名空间下,对图像文件、压缩文件等二进制数据进行读取操作时使用该类。

BinaryWirter

在System.IO命名空间下,对图像文件、压缩文件等二进制数据进行写入操作时使用该类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是刘彦宏吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值