在开发上位机的经历中,会有很多需要和下位机交互通信的场景,大多数都会定义一个和硬件的通信协议,最终在上位机代码中的形式其实就是符合通信协议的字节数组。
场景
在控制一些车辆进行货物搬运的业务场景下,我们需要即时的获取小车的状态数据,并且做出解析,最后进行业务处理。不管与下位机是如何通信的,最终都会读取到一个字节数组在内存中。以TCP通讯为例子,一般会在通讯协议的报文头中定义报文的长度,从而解决一些通讯问题,如粘包等,最后读取到正文部分。
如何解析字节数组到类或结构体中
建立与通信协议一致的结构体
比如通讯协议的正文格式定义如下:
![](data:image/png;base64,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)
1.id 小车编号
2.motor_steps 小车行走累计马达步数
3.speed 小车当前速度
在代码中建立对应的结构体
[StructLayout(LayoutKind.Explicit)]
public struct VehicleStatus
{
[FieldOffset(0)] public byte id;
[FieldOffset(1)] public ushort motor_steps;
[FieldOffset(3)] public byte speed;
}
使用不安全代码将字节数组映射到结构体中
byte[] metaData = new byte[4] { 10, 88, 89, 5 }; //模拟一段报文
unsafe
{
fixed (byte* metaPointer = metaData)
{
VehicleStatus* status = (VehicleStatus*)metaPointer;
// 堆代码 duidaima.com
Console.WriteLine($"小车编号:{status->id}");
Console.WriteLine($"小车速度:{status->speed}");
Console.WriteLine($"小车马达步数:{status->motor_steps}");
}
}
代码解释
StructLayout
表示某个类或者结构体里的成员的排列方式,这里我们使用LayoutKind.Explicit精确模式,该模式必须配合FieldOffset属性一起制定字段的物理内存排列位置。
fixed
用来钉住可移动变量,确保GC在执行期间对不会重新定位或释放包含对象实例,如果位置变了或者被释放了,谁还管你的非托管的指针对象呢?这边字节数组肯定是一个可移动变量了。