流的inputStream和OUtputStream和Read和Writer總是搞混,原來是我沒理清流程,流的產生到消失過程:
數據 —> 通過inputStream()、Read()轉化為流 —> 通過outputStream()、Writer()轉化為數據,就像流是通過一個小細管道傳輸的,input輸入管道變為流,output輸出管道變為數據,流類讀取數據為流,流將流寫入數據。
C# 中的流
流
抽象基類 Stream 支持讀取和寫入字節。 所有表示流的類都繼承自 Stream 類。 Stream 類及其派生類提供數據源和存儲庫的常見視圖,使程序員不必了解操作系統和基礎設備的具體細節。
流涉及三個基本操作:
讀取 - 將數據從流傳輸到數據結構(如字節數組)中。
寫入 - 將數據從數據源傳輸到流。
查找 - 對流中的當前位置進行查詢和修改。
根據基礎數據源或存儲庫,流可能只支持這些功能中的一部分。 例如,PipeStream 類不支持查找。 流的 CanRead、CanWrite 和 CanSeek 屬性指定流支持的操作。
下面是一些常用的流類:
- FileStream - 用於對文件進行讀取和寫入操作。
- IsolatedStorageFileStream - 用於對獨立存儲中的文件進行讀取或寫入操作。
- MemoryStream - 用於作為后備存儲對內存進行讀取和寫入操作。
- BufferedStream - 用於改進讀取和寫入操作的性能。
- NetworkStream - 用於通過網絡套接字進行讀取和寫入。
- PipeStream - 用於通過匿名和命名管道進行讀取和寫入。
- CryptoStream - 用於將數據流鏈接到加密轉換。
這里主要介紹后備存儲為內存的流:MemoryStream
主要介紹方法:
yier[index]).ToString("X2");//byte轉化為兩個16進制的數。X為16進制,2為 每次都是兩位數,即10轉化為0x0A而不是0xA。
注意:16進制的數和16位整數是不一樣的,16進制數字占4位,半個字節,即一個字節轉化為2個16進制的數,16位整數是short類型的數對應兩個字節。
public override void Write(byte[] buffer, int offset, int count);//byte[]寫入流,然后流中就有Length和Position了。
public override void WriteByte(byte value); //逐字節將緩存區寫入流,流的Length逐字節增加,和position逐漸增加。
public override long Seek(long offset, SeekOrigin loc); //設置position位置,
public override int Read(byte[] buffer, int offset, int count);//讀取流中數據到緩沖區,offset+count不能超過緩沖數組的Length
public override int ReadByte();//ReadByte從當前流讀取一個字節,返回將字節轉換為Int32,或者-1(如果已經到達流的末端)。ToByte將int轉化為字節
public virtual byte[] GetBuffer();//獲取創建該流的字節數組,后邊流數據改變了,此數組也會改變。因為此不是基本類型也不是string不可變類型。
//將漢字轉化為流,和將流解析為漢字,編碼規則要對應一致,否則解析出來亂碼。
byte[] secondString2 = Encoding.UTF8.GetBytes("上山打老虎66");
Console.WriteLine(Encoding.UTF8.GetString(secondString2));//上山打老虎66,加密和解碼編碼字符集要一致。
Array.Copy(secondString, 0, testArrayCopy, 3, 14);//從源數組的0位置開始復制,復制到目標數組從0位置開始,復制14個元素。目標數組長度必須大於17。
ushort opcode = BitConverter.ToUInt16(memoryStream.GetBuffer(), Packet.OpcodeIndex); //從startIndex位置開始解析之后的兩個字節 為16位無符號整數。,即返回轉化了的opcode。opcode(ushort)兩個字節,開始於下標1。
public static void Main() { byte[] array = new byte[1]; //定義一組數組array array = System.Text.Encoding.ASCII.GetBytes("dc"); //string轉換的字母對應的ASCALL碼 string d = Encoding.UTF8.GetString(array); //dc array = System.Text.Encoding.UTF8.GetBytes("一二"); //string轉換的字母對應的六個byte數組。三個字節一個漢字。 string d2 = Encoding.UTF8.GetString(array); //一二 array = System.Text.Encoding.Unicode.GetBytes("撒呢過"); //string轉換的漢字對應的6個byte數組。兩個字節一個漢字 string d3 = Encoding.Unicode.GetString(array); //撒呢過 UnicodeEncoding uniEncoding = new UnicodeEncoding(); byte[] yier = uniEncoding.GetBytes("一二"); //字節byte[]轉化為16進制。 StringBuilder strBuider = new StringBuilder(); for (int index = 0; index < yier.Length; index++) { strBuider.Append(((int)yier[index]).ToString("X2"));// } Console.WriteLine("一二的字節碼轉化為16進制:"+ strBuider.ToString());///結果:004E8C4E,而一二的Unicode編碼是:\u4E00\u4E8C,即每16位表示一個漢字, ///但是在將 UNICODE字符編碼的內容轉換為漢字的時候,字符是從后面向前處理的,所以要得到漢字需要將前8位和后8位字符調換組合和“\u”得到漢字的編碼。 int count; byte[] byteArray; char[] charArray; // Create the data to write to the stream. byte[] firstString = uniEncoding.GetBytes("一二三四五"); byte[] secondString = uniEncoding.GetBytes("上山打老虎66"); using (MemoryStream memStream = new MemoryStream(100)) { byte[] getBuff = memStream.GetBuffer(); //獲取創建該流的字節數組,后邊流數據改變了,此數組也會改變。因為此不是基本類型也不是string不可變類型。 // Write the first string to the stream. memStream.Write(firstString, 0, firstString.Length);//byte[]寫入流,然后流中就有Length和Position了。 //byte[] getBuff2 = memStream.GetBuffer(); //MemoryStream memStream2 = new MemoryStream(); //MemoryStream memStream3 = new MemoryStream(secondString); //byte[] getBuff3 = memStream2.GetBuffer();//{byte[0]} //byte[] getBuff4 = memStream3.GetBuffer();//未經授權的訪問異常:內存流的內部緩沖區不能被訪問異常。 // Write the second string to the stream, byte by byte. count = 0; while (count < secondString.Length) { memStream.WriteByte(secondString[count++]);//逐字節將將緩存區寫入流,流的Length逐字節增加,和position逐漸增加。 } // Write the stream properties to the console. Console.WriteLine("Capacity={0},Length={1},Position={2}\n", memStream.Capacity.ToString(), memStream.Length.ToString(), memStream.Position.ToString()); // Set the position to the beginning of the stream. memStream.Seek(0, SeekOrigin.Begin);//設置position=0 //memStream.Seek(5, SeekOrigin.Current);//設置position=0 //memStream.Seek(3, SeekOrigin.Begin);//設置position=3 //memStream.Seek(3, SeekOrigin.Current);//設置position=6 = 3+3 // Read the first 20 bytes from the stream. byteArray = new byte[memStream.Length]; count = memStream.Read(byteArray, 0, 20);//offset+count不能超過數組的Length //count = memStream.Read(byteArray, 0, 10);//讀取流中數據到byteArray緩沖區。則從流的當前位置開始讀取,一直讀到流結束或count個字節。 // Read the remaining bytes, byte by byte. while (count < memStream.Length)//即當未讀完流時 { //ReadByte從當前流讀取一個字節,返回將字節轉換為Int32,或者-1(如果已經到達流的末端)。ToByte將int轉化為字節 byteArray[count++] = Convert.ToByte(memStream.ReadByte()); } Console.WriteLine(Encoding.Unicode.GetString(byteArray));//一二三四五上山打老虎66 Console.WriteLine(Encoding.UTF8.GetString(byteArray));//亂碼 byte[] secondString2 = Encoding.UTF8.GetBytes("上山打老虎66"); Console.WriteLine(Encoding.UTF8.GetString(secondString2));//上山打老虎66,加密和解碼編碼字符集要一致。 byte[] testArrayCopy = new byte[20]; Array.Copy(secondString, 0, testArrayCopy, 3, 14);//從源數組的0位置開始復制,復制到目標數組從0位置開始,復制14個元素。目標數組長度必須大於17。 charArray = new char[uniEncoding.GetCharCount(testArrayCopy, 3, 14)]; uniEncoding.GetDecoder().GetChars(testArrayCopy, 3, 14, charArray, 0);//decoder解碼器,可解析byte[]數組中一部分元素, Console.WriteLine(Encoding.Unicode.GetString(byteArray)); //上山打老虎66 } }
ET框架中:將信息轉化為流,和將流解析成所需內容
SerializeTo
((Google.Protobuf.IMessage) message).WriteTo(stream);或
MongoPacker.SerializeTo(object obj, MemoryStream stream)
DeserializeFrom
MemoryStream stream = session.Stream; ///第一個參數:流中新位置,相對於第二個參數,第一個參數可正可負 stream.Seek(Packet.MessageIndex, SeekOrigin.Begin);//當前流位置設置為指定的值。 stream.SetLength(Packet.MessageIndex); byte[] b = stream.GetBuffer(); session.Network.MessagePacker.SerializeTo(new C2G_LoginGate() { Key = 1, RpcId = 1 }, stream);//對象序列化為流 stream.Seek(Packet.MessageIndex, SeekOrigin.Begin);//當前流位置設置為指定的值。從對應實例開始的流位置開始 才能正確解析。 object message = session.Network.MessagePacker.DeserializeFrom(Activator.CreateInstance(typeof(C2G_LoginGate)), stream);//對象序列化為流
Packet類
public static class Packet { public const int PacketSizeLength2 = 2;//引用的地方,傳遞的是可選參數 默認是2 public const int PacketSizeLength4 = 4;//NetInnerComponent組件Awake傳遞是4,外網是默認的2。 public const int FlagIndex = 0; public const int OpcodeIndex = 1;//Opcode下標開始位置,因為0位置存的是flag, public const int MessageIndex = 3;//消息內容下標開始位置,因為0位置flag,1、2位置Opcode。opcode是short類型對應兩個字節 }

