在開發上位機的經歷中,會有很多需要和下位機交互通信的場景,大多數都會定義一個和硬件的通信協議,最終在上位機代碼中的形式其實就是符合通信協議的字節數組。
場景
在控制一些車輛進行貨物搬運的業務場景下,我們需要即時的獲取小車的狀態數據,並且做出解析,最後進行業務處理。不管與下位機是如何通信的,最終都會讀取到一個字節數組在內存中。
以TCP通訊爲例子,一般會在通訊協議的報文頭中定義報文的長度,從而解決一些通訊問題,如粘包等,最後讀取到正文部分。
如何解析字節數組到類或結構體中
建立與通信協議一致的結構體
比如通訊協議的正文格式定義如下:
id | motor_steps | speed |
---|---|---|
1字節 | 2字節 | 1字節 |
- id 小車編號
- motor_steps 小車行走累計馬達步數
- 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;
Console.WriteLine($"小車編號:{status->id}");
Console.WriteLine($"小車速度:{status->speed}");
Console.WriteLine($"小車馬達步數:{status->motor_steps}");
}
}
代碼解釋
StructLayout
表示某個類或者結構體裏的成員的排列方式,這裏我們使用LayoutKind.Explicit精確模式,該模式必須配合FieldOffset屬性一起制定字段的物理內存排列位置。
fixed
用來釘住可移動變量,確保GC在執行期間對不會重新定位或釋放包含對象實例,如果位置變了或者被釋放了,誰還管你的非託管的指針對象呢?這邊字節數組肯定是一個可移動變量了。