MemoryStream

被MemoryStream狠狠地坑了一把

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

应用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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后终于找到问题的根源...

1
2
3
4
5
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             try
34             {
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 }
复制代码

总结

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

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

原文地址:https://www.cnblogs.com/Leo_wl/p/3106267.html