http://www.haogongju.net/art/624248
在写C#TCP通信程序时,发送数据时,只能发送byte数组,处理起来比较麻烦不说,如果是和VC6.0等写的程序通信的话,很多的都是传送结构体,在VC6.0中可以很方便的把一个char[]数组转换为一个结构体,而在C#却不能直接把byte数组转换为结构体,要在C#中发送结构体,可以按以下方法实现:
(1)定义结构体:
//命名空间
using System.Runtime.InteropServices;
//注意这个属性不能少
[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi,Pack=1)]
struct TestStruct
{
public int c;
//字符串,SizeConst为字符串的最大长度
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string str;
//int数组,SizeConst表示数组的个数,在转换成
//byte数组前必须先初始化数组,再使用,初始化
//的数组长度必须和SizeConst一致,例test = new int[6];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public int[] test;
}
(2)结构体转byte数组:
////
/// 结构体转byte数组
///
/// 要转换的结构体
///
public static byte[] StructToBytes(object structObj)
{
//得到结构体的大小
int size = Marshal.SizeOf(structObj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(structObj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, 0, size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
}
(3)byte数组转结构体:
///
/// byte数组转结构体
///
/// byte数组
/// 结构体类型
///
public static object BytesToStuct(byte[] bytes,Type type)
{
//得到结构体的大小
int size = Marshal.SizeOf(type);
//byte数组长度小于结构体的大小
if (size > bytes.Length)
{
//返回空
return null;
}
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将byte数组拷到分配好的内存空间
Marshal.Copy(bytes,0,structPtr,size);
//将内存空间转换为目标结构体
object obj = Marshal.PtrToStructure(structPtr, type);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回结构体
return obj;
}
尽管在C#中结构与类有着惊人的相似度,但在实际应用中,会常常因为一些特殊之类而错误的使用它,下面几点内容是笔者认为应该注意的:
对于结构
1)可以有方法与属性
2)是密封的,不能被继承,或继承其他结构
3)结构隐式地继承自System.ValueType
4)结构有默认的无参数构造函数,可以将每个字段初始化为默认值,但这个默认的构造函数不能被替换,即使重载了带参数的构造函数
5)结构没有析构函数
6)除了const成员外,结构的字段不能在声明结构时初始化
7)结构是值类型,在定义时(尽管也使用new运算符)会分配堆栈空间,其值也存储于堆栈
8)结构主要用于小的数据结构,为了更好的性能,不要使用过于庞大的结构
9)可以像类那样为结构提供 Close() 或 Dispose() 方法
如果经常做通信方面的程序,结构体是非常有用的(为了更有效地组织数据,建议使用结构体),也会遇到字节数据与结构体相互转化的问题,下面是一般解决方法:
如何定义一个按字节顺序存储的结构体?
1 [StructLayout(LayoutKind.Sequential, Pack = 1)] //顺序排列,并按1字节对齐 (这里保存的是一组飞机参数,共73字节) 2public struct StructPlane 3 {
4public byte serialNum; 5public double pitch; 6public double roll; 7public double yaw; 8public double pitchVel; 9public double rollVel; 10public double yawVel; 11public double alt; 12public double vz; 13public ushort pwm1; 14public ushort pwm2; 15public ushort pwm3; 16public ushort pwm4; 17 };
结构体转字节数组的方法:
注:
1. 一般PC用小端模式(即高位存放于高地址,低位存放于低地址,反之为大端模式)存放数据,如果另一通讯设备用大端存储数据,则相互转化时就要注意了。
2. 结构体需按上面的方式,顺序排列并按1字节对齐,下面的所有方法都一样。
1 //需添加的引用,提供Marshal类 2 using System.Runtime.InteropServices; 3 4///5/// 结构体转字节数组(按小端模式) 6/// 7/// struct type 8/// 9byte[] StructureToByteArray(object obj) 10 { 11int len = Marshal.SizeOf(obj); 12byte[] arr = new byte[len]; 13 IntPtr ptr = Marshal.AllocHGlobal(len); 14 Marshal.StructureToPtr(obj, ptr, true); 15 Marshal.Copy(ptr, arr, 0, len); 16 Marshal.FreeHGlobal(ptr); 17return arr; 18 } 19 20 /// 21/// 结构体转字节数组(按大端模式) 22/// 23/// struct type 24/// 25byte[] StructureToByteArrayEndian(object obj) 26 { 27object thisBoxed = obj; //copy ,将 struct 装箱 28 Type test = thisBoxed.GetType(); 29 30int offset = 0; 31byte[] data = new byte[Marshal.SizeOf(thisBoxed)]; 32 33object fieldValue; 34 TypeCode typeCode; 35byte[] temp; 36// 列举结构体的每个成员,并Reverse 37foreach (var field in test.GetFields()) 38 { 39 fieldValue = field.GetValue(thisBoxed); // Get value 40 41 typeCode = Type.GetTypeCode(fieldValue.GetType()); // get type 42 43switch (typeCode) 44 { 45case TypeCode.Single: // float 46 { 47 temp = BitConverter.GetBytes((Single)fieldValue); 48 Array.Reverse(temp); 49 Array.Copy(temp, 0, data, offset, sizeof(Single)); 50break; 51 } 52case TypeCode.Int32: 53 { 54 temp = BitConverter.GetBytes((Int32)fieldValue); 55 Array.Reverse(temp); 56 Array.Copy(temp, 0, data, offset, sizeof(Int32)); 57break; 58 } 59case TypeCode.UInt32: 60 { 61 temp = BitConverter.GetBytes((UInt32)fieldValue); 62 Array.Reverse(temp); 63 Array.Copy(temp, 0, data, offset, sizeof(UInt32)); 64break; 65 } 66case TypeCode.Int16: 67 { 68 temp = BitConverter.GetBytes((Int16)fieldValue); 69 Array.Reverse(temp); 70 Array.Copy(temp, 0, data, offset, sizeof(Int16)); 71break; 72 } 73case TypeCode.UInt16: 74 { 75 temp = BitConverter.GetBytes((UInt16)fieldValue); 76 Array.Reverse(temp); 77 Array.Copy(temp, 0, data, offset, sizeof(UInt16)); 78break; 79 } 80case TypeCode.Int64: 81 { 82 temp = BitConverter.GetBytes((Int64)fieldValue); 83 Array.Reverse(temp); 84 Array.Copy(temp, 0, data, offset, sizeof(Int64)); 85break; 86 } 87case TypeCode.UInt64: 88 { 89 temp = BitConverter.GetBytes((UInt64)fieldValue); 90 Array.Reverse(temp); 91 Array.Copy(temp, 0, data, offset, sizeof(UInt64)); 92break; 93 } 94case TypeCode.Double: 95 { 96 temp = BitConverter.GetBytes((Double)fieldValue); 97 Array.Reverse(temp); 98 Array.Copy(temp, 0, data, offset, sizeof(Double)); 99break; 100 } 101case TypeCode.Byte: 102 { 103 data[offset] = (Byte)fieldValue; 104break; 105 } 106default: 107 { 108//System.Diagnostics.Debug.Fail("No conversion provided for this type : " + typeCode.ToString()); 109break; 110 } 111 }; // switch 112if (typeCode == TypeCode.Object) 113 { 114int length = ((byte[])fieldValue).Length; 115 Array.Copy(((byte[])fieldValue), 0, data, offset, length); 116 offset += length; 117 } 118else 119 { 120 offset += Marshal.SizeOf(fieldValue); 121 } 122 } // foreach 123 124return data; 125 } // Swap
字节数组转结构体的方法:
////// 字节数组转结构体(按小端模式) /// /// 字节数组 /// 目标结构体 /// bytearray内的起始位置 public static void ByteArrayToStructure(byte[] bytearray, ref object obj, int startoffset) { int len = Marshal.SizeOf(obj); IntPtr i = Marshal.AllocHGlobal(len); // 从结构体指针构造结构体 obj = Marshal.PtrToStructure(i, obj.GetType()); try { // 将字节数组复制到结构体指针 Marshal.Copy(bytearray, startoffset, i, len); } catch (Exception ex) { Console.WriteLine("ByteArrayToStructure FAIL: error " + ex.ToString()); } obj = Marshal.PtrToStructure(i, obj.GetType()); Marshal.FreeHGlobal(i); //释放内存,与 AllocHGlobal() 对应 } /// /// 字节数组转结构体(按大端模式) /// /// 字节数组 /// 目标结构体 /// bytearray内的起始位置 public static void ByteArrayToStructureEndian(byte[] bytearray, ref object obj, int startoffset) { int len = Marshal.SizeOf(obj); IntPtr i = Marshal.AllocHGlobal(len); byte[] temparray = (byte[])bytearray.Clone(); // 从结构体指针构造结构体 obj = Marshal.PtrToStructure(i, obj.GetType()); // 做大端转换 object thisBoxed = obj; Type test = thisBoxed.GetType(); int reversestartoffset = startoffset; // 列举结构体的每个成员,并Reverse foreach (var field in test.GetFields()) { object fieldValue = field.GetValue(thisBoxed); // Get value TypeCode typeCode = Type.GetTypeCode(fieldValue.GetType()); //Get Type if (typeCode != TypeCode.Object) //如果为值类型 { Array.Reverse(temparray, reversestartoffset, Marshal.SizeOf(fieldValue)); reversestartoffset += Marshal.SizeOf(fieldValue); } else//如果为引用类型 { reversestartoffset += ((byte[])fieldValue).Length; } } try { //将字节数组复制到结构体指针 Marshal.Copy(temparray, startoffset, i, len); } catch (Exception ex) { Console.WriteLine("ByteArrayToStructure FAIL: error " + ex.ToString()); } obj = Marshal.PtrToStructure(i, obj.GetType()); Marshal.FreeHGlobal(i); //释放内存 }
使用示例一:
... ... byte[] packet = new byte[73]{...}; StructPlane structPlane = new StructPlane(); object structType = structPlane; ByteArrayToStructure(packet, ref structType, 0);
使用示例二:
StructPlane structPlane = new StructPlane(); structPlane.serialNum = ...; structPlane.time = ...; structPlane.pitch = ...; ... ... byte[] datas = StructureToByteArray(structPlane);
http://www.haogongju.net/art/1200066