流(stream)是對串行傳輸?shù)臄?shù)據(jù)(以字節(jié)為單位)的一種抽象表示,底層的設備可以是文件、外部設備、主存、網(wǎng)絡套接字等。

流提供三種基本操作:
- 寫入:將數(shù)據(jù)從內(nèi)存緩沖區(qū)傳輸?shù)酵獠吭础?/li>
- 讀?。簩?shù)據(jù)從外部源傳輸?shù)絻?nèi)存緩沖區(qū)。
- 查找:重新設置流的當前位置,以便隨機讀寫。需要注意的是,并不是所有的流類型都能夠支持查找,例如,網(wǎng)絡流沒有當前位置的統(tǒng)一概念,因此一般不支持查找。
說明:Stream類提供有多種操作流的方法,其中Read和Write方法是Stream類及其派生類都提供的實現(xiàn),可支持在字節(jié)級別上對數(shù)據(jù)進行讀寫。實際的程序開發(fā)中,僅支持字節(jié)級別的數(shù)據(jù)處理會給開發(fā)人員帶來不便。
提供字符串或二進制方式讀取或寫入流。
優(yōu)點:
方法更靈活
部分方法可解決TCP消息通信無邊界問題

FileStream類
FileStream類繼承于Stream類,一個FileStream類的實例實際上代表一個磁盤文件,使用FileStream類可以對文件系統(tǒng)上的文件進行讀取、寫入、打開和關閉操作。
1、創(chuàng)建FileStream實例
(1)常用的構造函數(shù)具有三個參數(shù),例如:
FileStream(string path,FileMode mode FileAccess access)
-
FileMode值用于指定當文件不存在時是否創(chuàng)建該文件,并確定是保留還是改寫現(xiàn)有文件的內(nèi)容
image.png -
FileAccess值是枚舉的一個成員,它控制對文件的訪問權限。表4-10列出了FileAccess所有的枚舉形式
image.png
(2)File和FileInfo類也提供了創(chuàng)建FileStream對象的方法。
其中,OpenRead方法返回只讀文件流,OpenWrite方法返回只寫文件流。
例如:FileStream fs= File.OpenRead("C:\File1.txt");
2. 讀文件
在獲取FileStream實例之后,可利用FileStream對象的Read方法讀取文件中的數(shù)據(jù)。該方法用于從流中讀取字節(jié)塊并將該數(shù)據(jù)寫入給定字節(jié)數(shù)組中。其語法形式為:
public override int Read(byte[] array,int offset,int count)
array : 存儲從文件流中讀取的數(shù)據(jù)。
offset : array字節(jié)數(shù)組中開始寫入數(shù)據(jù)的下標,一般為0。
size : 要從文件流中讀出字節(jié)的大小
返回值: 從FileStream中讀取的字節(jié)數(shù)。
3. 寫文件
Stream類及其所有子類都提供了Write方法,F(xiàn)ileStream類也不例外。該方法可將字節(jié)數(shù)組寫入流。語法形式為
public override void Write (
byte[] buffer, //包含要寫入流的數(shù)據(jù)
int offset, // buffer中開始寫入數(shù)據(jù)的位置
int size //要寫入流的字節(jié)數(shù)
)
MemoryStream類
MemoryStream類表示的是保存在內(nèi)存中的數(shù)據(jù)流。由內(nèi)存流封裝的數(shù)據(jù)可以在內(nèi)存中直接訪問。
MemoryStream類的構造函數(shù)具有多種重載形式,常用的構造函數(shù)有:
(1)MemoryStream ()
該構造函數(shù)初始分配的容量大小為0,隨著數(shù)據(jù)的不斷寫入容量可以不斷擴展。
(2)MemoryStream (Byte[])
該構造函數(shù)獲取的MemoryStream實例根據(jù)Byte[]字節(jié)數(shù)組進行初始化,并且實例容量大小固定即為字節(jié)數(shù)組的長度。由于實例的容量不能擴展,該構造函數(shù)一般用于數(shù)據(jù)不發(fā)生變化的場合。
String testdata = "測試數(shù)據(jù)";
char[] chars = testdata.ToCharArray();
Byte[] bytes = new Byte[encoder.GetByteCount(chars, 0, chars.Length, true)];
MemoryStream mem = new MemoryStream(bytes);
(3)MemoryStream (int capacity)
通過該構造函數(shù)創(chuàng)建初始容量大小為capacity的實例,并且實例容量大小可擴展。
網(wǎng)絡流
在System.Net.Sockets名稱空間中有一個NetworkStream類,用于通過網(wǎng)絡套接字發(fā)送和接收數(shù)據(jù)。
NetworkStream類支持對網(wǎng)絡數(shù)據(jù)的同步或異步訪問,它可被視為在數(shù)據(jù)來源端和接收端之間架設了一個數(shù)據(jù)通道.
只用于面向連接的數(shù)據(jù)傳輸

寫入操作是指從來源端內(nèi)存緩沖區(qū)到網(wǎng)絡上的數(shù)據(jù)傳輸;
讀取操作是從網(wǎng)絡上到接收端內(nèi)存緩沖區(qū)(如字節(jié)數(shù)組)的數(shù)據(jù)傳輸。

NetworkStream的用法
1、構造NetworkStream:
(1)利用TcpClient獲取網(wǎng)絡流對象,例如:
TcpClient tcpClient=new TcpClient();
tcpClient.Connect("www.abcd.com", 51888);
NetworkStream networkStream = client.GetStream();
(2)利用Socket獲取網(wǎng)絡流對象,例如:
NetworkStream myNetworkStream = new NetworkStream(mySocket);
2、發(fā)送數(shù)據(jù)
public override void Write (byte[] buffer,int offset,int size )

3、接收數(shù)據(jù)
public override int Read ([InAttribute] [OutAttribute] byte[] buffer,int offset,int size)
各參數(shù)的含義:
buffer :內(nèi)存中用于存儲從NetworkStream讀取的數(shù)據(jù)的位置。
offset:buffer 中開始將數(shù)據(jù)存儲到的位置。
Size:要從NetworkStream中讀取的字節(jié)數(shù)。
返回值 :
實際從NetworkStream中讀取的字節(jié)數(shù)。
Write方法:
- NetworkStream對象的Write方法的返回值為void,該對象之所以不返回實際發(fā)送的字節(jié)數(shù),是因為能保證字節(jié)數(shù)組中的數(shù)據(jù)全部發(fā)送到TCP發(fā)送緩沖區(qū)中。
- 在使用NetworkStream對象的Write方法前最好先檢測NetworkStream對象的Writeable屬性是否為True。
- 如果發(fā)送的全部是單行文本信息,創(chuàng)建NetworkStream對象后,使用StreamReader和StreamWriter的ReadLine和WriteLine方法更簡單。
Read方法:
- 調(diào)用NetworkStream類的Read方法前應確保NetworkStream對象的CanRead屬性值有效
- 由于有可能TCP接收緩沖區(qū)還沒有接收到對方發(fā)送過來的指定長度的數(shù)據(jù),所以Read方法有一個整型的返回值。
- 如果遠程主機關閉了套接字連接,并且此時有效數(shù)據(jù)已經(jīng)被完全接收,那么Read方法的返回值將會是0字節(jié)。
使用NetworkStream對象時,需要注意:
(1)通過DataAvailable屬性,可以迅速查看在緩沖區(qū)中是否有數(shù)據(jù)等待讀出。
(2)網(wǎng)絡流沒有當前位置的概念,因此它不支持對數(shù)據(jù)流的查找和隨機訪問,NetworkStream對象的CanSeek屬性始終返回false,讀取Position屬性和調(diào)用Seek方法時,都會引發(fā)NotSupportedException異常。
(3)網(wǎng)絡數(shù)據(jù)傳輸完成后,不要忘記用Close方法關閉NetworkStream對象。
StreamWriter與StreamReader類
可以看到所有的NetworkStream、MemoryStream、FileStream類都提供了以字節(jié)為基本單位的讀寫方法,但是這種方法需要首先將待寫入的數(shù)據(jù)轉化為字節(jié)數(shù)組后才能夠進行讀寫,當操作的是使用字符編碼的文本數(shù)據(jù)時,給開發(fā)人員帶來了不便。
StreamReader類主要完成以一種特定的編碼從流中讀取字符的功能,一般用于對文本數(shù)據(jù)的讀取操作;
StreamWriter類則主要以特定的編碼向流中寫入字符,一般用于對文本數(shù)據(jù)的寫操作。
1、創(chuàng)建StreamWriter實例
(1)StreamWriter (String path)
根據(jù)文件路徑創(chuàng)建以UTF-8編碼StreamWriter對象。例如:
StreamWriter sw= new StreamWriter ("C:\file1.txt");
(2)File及FileInfo類提供的CreateText方法。例如:
StreamWriter sw = File.CreateText ("C:\file1.txt");
(3)StreamWriter (Stream stream)
直接使用流對象創(chuàng)建StreamWriter對象。例如:
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter (fs);
如果已經(jīng)有了網(wǎng)絡流對象,同樣可以直接對網(wǎng)絡流對象進行封裝。
NetworkStream networkStream = client.GetStream();
StreamWriter sw = new StreamWriter (networkStream);
2、寫入文本
利用StreamWriter類將以一種特定的編碼向流中寫入字符。常見方法如下所示:
Write():向數(shù)據(jù)流寫入數(shù)據(jù)。Write()方法只是把傳送給它的字符串寫入流,但不追加換行符,因此可以使用Write()語句寫入完整的句子或段落。
WriteLine():向數(shù)據(jù)流寫入指定數(shù)據(jù)和一個換行符。
Close():關閉流。
3.創(chuàng)建StreamReader實例
(1)SteamReader (Stream stream)
利用流對象創(chuàng)建SteamReader對象。例如:
NetworkStream networkStream = client.GetStream();
SteamReader sr = new SteamReader (networkStream)
(2)SteamReader (String path)
如果需要處理的是文件流,則可以根據(jù)文件路徑創(chuàng)建一個以UTF8編碼的SteamReader對象。例如:
SteamReader sr = new SteamReader ("C:\file1.txt");
4.讀取文本
利用StremReader將以一種特定的編碼從流中讀取字符。常用方法有:
ReadLine():讀取數(shù)據(jù)直到遇到換行符(Unix)或者回車換行(Windows)為止。
ReadToEnd():讀取到文件尾的全部數(shù)據(jù)。
Peek():返回數(shù)據(jù)中下一個可用字符的編碼值,如到達文件末尾則Peek()方法的值為-1。
Close():關閉流,使用StreamReader之后,需要調(diào)用Close方法關閉流。
BinaryReader和BinaryWriter類
提供了BinaryReader和BinaryWriter類,用于以二進制模式讀寫流。
它們提供的一些讀寫方法是對稱的,比如針對不同的數(shù)據(jù)結構,BinaryReader提供了ReadByte、ReadBoolean、ReadInt、ReadInt16、ReadDouble、ReadString等方法,與之對應BinaryWriter則提供了多個重載Write方法。例如當Write方法傳遞的參數(shù)為Int32類型時,BinaryWriter類的Write方法將Int32類型數(shù)據(jù)轉化為長度為4的字節(jié)數(shù)組,并將字節(jié)流傳遞給一個Stream對象。

