C#流之——MemoryStream ,常用方法用法测试

流的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类型对应两个字节
    }

 

 

posted @ 2020-04-23 15:55  好Wu赖  阅读(3216)  评论(0编辑  收藏  举报