博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
被MemoryStream狠狠地坑了一把
阅读量:5945 次
发布时间:2019-06-19

本文共 8083 字,大约阅读时间需要 26 分钟。

Stream是.net数据流操作的一个封装,它提供统一的流读写规则,为后期开发这方面的功能提供了很大的便利性.有些场景下是直接操作byte[]比较灵活所以Stream派生出MemoryStream从byte[]构建一个stream来方便开发人员使用.但在使用的时候碰到了一个非常坑爹事情.一个非常意想不到的结果...

应用代码

string value = "111111111";            string value1 = "2222222222222222222222";            System.IO.MemoryStream stream = new System.IO.MemoryStream(mBuffer);            int count = Encoding.UTF8.GetBytes(value, 0, value.Length, mBuffer, 0);            stream.Position = 0;            stream.SetLength(count);            Console.WriteLine("Length:"+count);            Console.WriteLine(Encoding.UTF8.GetString(mBuffer, 0, count));            count = Encoding.UTF8.GetBytes(value1, 0, value1.Length, mBuffer, 0);            stream.Position = 0;            stream.SetLength(count);            Console.WriteLine("Length:" + count); ;            Console.WriteLine(Encoding.UTF8.GetString(mBuffer, 0, count));            Console.Read();

以上代码是把不同长度的字符编码到buffer中,然后再设置对应stream的开始位置和长度,从而让stream提供给其他功能使用,比较常见的就是对象反序列化等工作.从代码来看结果输出内容分别value和value1,但最终的运行结果确是

value1对应的内容少了一截...当出现这问题的时候排查了很久数据跟踪但没发现有任何环节有异常,因为buffer在后期根本没有地方对它进行修改,但数据确发生改变.

MemoryStream的一个坑

在跟踪日志来看buffer在经过stream.setlength之前都是好的,但经过这个方法后buffer内容就改变了,后面的代码也没对stream进行任何的操作;所以马上想到地扩容的问题,但由于buffer的长度比较大对应setlength的值也不可能大于buffer分配的长度问题应该不是扩容导致的;无耐之下只好反编译MomeryStream的代码看下,仔细查看MomeryStream的setlength后终于找到问题的根源...

int num = this._origin + (int)value;	if (!this.EnsureCapacity(num) && num > this._length)	{		Array.Clear(this._buffer, this._length, num - this._length);	}

这代码说明了一切问题,在setlength里如果没有导致扩容和大于之前的长度,则会增长部分进行一个清除操作...

实际应用中使用的代码

1 namespace MSMQNode.Agent 2 { 3     public class ProtoBufFormater : IObjectFormater 4     { 5         public object Deserialize(ByteArraySegment content) 6         { 7             object result; 8             try 9             {10                 content.SetPostion(0);11                 string text = content.ReadShortString(Encoding.UTF8);12                 Type type = Type.GetType(text);13                 if (type == null)14                 {15                     throw MQError.TYPE_NOTFOUND(text);16                 }17                 object obj = Activator.CreateInstance(type);18                 Stream stream = content.GetStream();19                 stream.Position = (long)content.Postion;20                 stream.SetLength((long)content.Count);21                 obj = RuntimeTypeModel.Default.Deserialize(stream, null, type);22                 result = obj;23             }24             catch (Exception error)25             {26                 throw new MQMessageFormatError("Deserialize Error!", error);27             }28             return result;29         }30         public ByteArraySegment Serialize(object message)31         {32             ByteArraySegment byteArraySegment = HttpDataPackage.BufferPool.Pop();33             try34             {35                 Type type = message.GetType();36                 string typeName = Utils.GetTypeName(type);37                 byteArraySegment.WriteShortString(typeName, Encoding.UTF8);38                 Stream stream = byteArraySegment.GetStream();39                 stream.Position = (long)byteArraySegment.Postion;40                 stream.SetLength((long)byteArraySegment.Postion);41                 RuntimeTypeModel.Default.Serialize(stream, message);42                 byteArraySegment.SetInfo(0, (int)stream.Length);43             }44             catch (Exception error)45             {46                 HttpDataPackage.BufferPool.Push(byteArraySegment);47                 throw new MQMessageFormatError("Serialize Error!", error);48             }49             return byteArraySegment;50         }51     }52 }

解决方法

解决以上问题的办法有几种:

1)在每次使用的时候都针对buffer创建新的MemoryStream

2)由MemoryStream重新写入流数据

3)实现一个简单的Stream,调整一下SetLength代码

针对自己的情况选择第三种方式是最简单,代码调整范围小又能达到不重复创建Stream的目的.直接反编译MemoryStream抄一把:)

1     public class ArraySegmentStream:System.IO.Stream  2     {  3         public ArraySegmentStream(byte[] data)  4         {  5             _buffer = data;  6             _length = 0;  7             _position = 0;  8             _origin = 0;  9         } 10  11         private byte[] _buffer; 12  13         private int _origin; 14  15         private int _position; 16  17         public override bool CanRead 18         { 19             get { return true; } 20         } 21  22         public override bool CanSeek 23         { 24             get { return true; } 25         } 26  27         public override bool CanWrite 28         { 29             get { return true; } 30         } 31  32         public override void Flush() 33         { 34              35         } 36  37         private int _length = 0; 38  39         public override long Length 40         { 41             get { return _length; } 42         } 43  44         public override long Position 45         { 46             get 47             { 48                 return _position; 49             } 50             set 51             { 52                 _position = (int)value; 53             } 54         } 55  56         public override int Read(byte[] buffer, int offset, int count) 57         { 58              59             int num = this._length - this._position; 60             if (num > count) 61             { 62                 num = count; 63             } 64             if (num <= 0) 65             { 66                 return 0; 67             } 68             if (num <= 8) 69             { 70                 int num2 = num; 71                 while (--num2 >= 0) 72                 { 73                     buffer[offset + num2] = this._buffer[this._position + num2]; 74                 } 75             } 76             else 77             { 78                 Buffer.BlockCopy(this._buffer, this._position, buffer, offset, num); 79             } 80             this._position += num; 81             return num; 82         } 83  84         public override long Seek(long offset, System.IO.SeekOrigin origin) 85         { 86             switch (origin) 87             { 88                 case SeekOrigin.Begin: 89                     { 90                         int num = this._origin + (int)offset; 91                          92                         this._position = num; 93                         break; 94                     } 95                 case SeekOrigin.Current: 96                     { 97                         int num2 = this._position + (int)offset; 98                         this._position = num2; 99                         break;100                     }101                 case SeekOrigin.End:102                     {103                         int num3 = this._length + (int)offset;104                        105                         this._position = num3;106                         break;107                     }108                 109             }110             return (long)this._position;111         }112 113         public override void SetLength(long value)114         {115             int num = this._origin + (int)value;116             this._length = num;117             if (this._position > num)118             {119                 this._position = num;120             }121         }122 123         public override void Write(byte[] buffer, int offset, int count)124         {125             126             int num = this._position + count;127             if (num > this._length)128             {129                 this._length = num;130             }131             if (count <= 8 && buffer != this._buffer)132             {133                 int num2 = count;134                 while (--num2 >= 0)135                 {136                     this._buffer[this._position + num2] = buffer[offset + num2];137                 }138             }139             else140             {141                 Buffer.BlockCopy(buffer, offset, this._buffer, this._position, count);142             }143             this._position = num;144         }145     }

总结

真的搞不明白为什么要这样设计,既然Length是可设置的即说明可以由开发人员指定现在流的内容长度,开发人员在设置之也会意识到相应buffer的数据信息.更何况Length的改变并不会更改postion位置,在后面对Stream的写入自然会把之前的内容代替.

如果那位同学以后要这样使用MemoryStream就要注意一下了:)

转载地址:http://ilwxx.baihongyu.com/

你可能感兴趣的文章
ADO.NET复习——自己编写SqlHelper类
查看>>
库函数strlen源码重现及注意问题
查看>>
《实例化需求》读书笔记
查看>>
常用Java8语法小结
查看>>
ZJOI2019 Day2 游记
查看>>
ccf题库中2015年12月2号消除类游戏
查看>>
WinForm窗体间如何传值
查看>>
Ado.Net 连接数据库
查看>>
java多线程系列1:Sychronized关键字
查看>>
解释性的语言vs编译性语言
查看>>
20155222 2016-2017-2 《Java程序设计》第10周学习总结
查看>>
MapReduce1.x与MapReduce2.x差异
查看>>
PHP array_key_exists() 函数(判断某个数组中是否存在指定的 key)
查看>>
Charpter5 软件测试总结
查看>>
python中@staticmethod、@classmethod和实例方法
查看>>
Java创建数组的三种方法
查看>>
管理计算机内存
查看>>
some requirement checks failed
查看>>
存储管理
查看>>
HDU-2089-不要62
查看>>