C#中实现byte[]与任意对象互换(服务端通讯专用)
C++中,我们可以非常方便的将网络通讯接收来的char*缓冲区转成任意类型的结构体,并从中提取必要信息,只需要一个结构体类型指针的强制转换即可。
但是在C#中,所有涉及到内存及指针的操作均被判定为不安全操作,使得上述机制的实现变得复杂化。
要在C#中便捷的实现网络通讯缓冲区byte[]与任意类型对象的相互转换,常用的方法大致有三:
1.序列化与反序列化
- public static byte[] ObjectToBytes(object obj)
- {
- using (MemoryStream ms = new MemoryStream())
- {
- IFormatter formatter = new BinaryFormatter();
- formatter.Serialize(ms, obj);
- return ms.GetBuffer();
- }
- }
- public static object BytesToObject(byte[] Bytes)
- {
- using (MemoryStream ms = new MemoryStream(Bytes))
- {
- IFormatter formatter = new BinaryFormatter();
- return formatter.Deserialize(ms);
- }
- }
注意:传入的结构体类型一定是“可序列化的(Serializable)”
优点:安全可靠
使用这种方法一般不会产生其他的副作用,其安全度在C#的可控范围之内。
缺点:浪费资源、效率偏低
使用这种方法会造成不必要的资源浪费:
struct原有大小 序列化之后的byte[]大小
100 256
800 1024
1000 2048
3000 4096
6000 8192
10000 16384
产生出不必要的冗余数据,效率的降低将是必然结果。
2.内存流配合BitConvert
BitConvert类提供了丰富的byte[]与其他基础类型(Int16、Int32等)间的互换方法。
byte[]转成特定struct时,需要实现new一个新的struct。之后借助BitConvert将接收来的byte[]缓冲区特定段按照struct各部分定义转化为相应类型,并赋值给struct成员。反之亦然。
优点:安全可靠,性能可观
使用这种方法一般不会产生其他的副作用,其安全度在C#的可控范围之内。
缺点:资源浪费,不易实现
使用这种方法造成了额外的内存申请与复制。
另外,针对每种特定类型的数据包,需要提供特定的包解析与生成机制。
可以考虑通过包类型间的继承关系降低后期维护的难度。比如:定义一种基类型的数据包,子类数据包继承基类数据包的成员,并重写基类的解析与生成方法等等。
3.Marshal
该类对外提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。
- public static byte[] StructToBytes(Object obj)
- {
- int size = Marshal.SizeOf(obj);
- byte[] bytes = new byte[size];
- IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
- Marshal.StructureToPtr(obj, arrPtr, true);
- return bytes;
- }
- public static Object BytesToStruct(byte[] bytes,Type StructStyle)
- {
- IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
- return Marshal.PtrToStructure(arrPtr, StructStyle);
- }
net 4.0 实现方法:
- /// <summary>
- /// 由结构体转换为byte数组
- /// </summary>
- public static byte[] StructureToByte<T>(T structure)
- {
- int size = Marshal.SizeOf(typeof(T));
- byte[] buffer = new byte[size];
- IntPtr bufferIntPtr = Marshal.AllocHGlobal(size);
- try
- {
- Marshal.StructureToPtr(structure, bufferIntPtr, true);
- Marshal.Copy(bufferIntPtr, buffer, 0, size);
- }
- finally
- {
- Marshal.FreeHGlobal(bufferIntPtr);
- }
- return buffer;
- }
- /// <summary>
- /// 由byte数组转换为结构体
- /// </summary>
- public static T ByteToStructure<T>(byte[] dataBuffer)
- {
- object structure = null;
- int size = Marshal.SizeOf(typeof(T));
- IntPtr allocIntPtr = Marshal.AllocHGlobal(size);
- try
- {
- Marshal.Copy(dataBuffer, 0, allocIntPtr, size);
- structure = Marshal.PtrToStructure(allocIntPtr, typeof(T));
- }
- finally
- {
- Marshal.FreeHGlobal(allocIntPtr);
- }
- return (T)structure;
- }
优点:高效、易实现
这个方法的实际效果与C++类似,不存在额外的内存申请及拷贝动作。
缺点:不安全、功能受限
1> 某些特殊情况下,该方法会失效。
2> 由于涉及到了与非托管内存间的转换,安全度降低。
3> C#中的Struct功能弱化,无法有效组织数据包继承关系。
4> 在一些特有环境下,Marshal的权限并未全部公开,比如Silverlight。
转 https://blog.csdn.net/qq_31967569/article/details/81166780
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)