解析的字节数组在解析成结构后变成垃圾

Posted

技术标签:

【中文标题】解析的字节数组在解析成结构后变成垃圾【英文标题】:Parsed byte array turns into garbage after being parsed into a struct 【发布时间】:2021-01-18 10:25:57 【问题描述】:

我正在通过 UDP 接收数据包,我将其解析为结构,在特定情况下,我的数据变得一团糟,我无法弄清楚问题可能是什么。让我概述一下我在做什么。

    UDP Receiver 接收数据包并将其传递给回调 - 此阶段字节数组的大小为 1307 字节,这是数据包的预期大小 字节数组被编组(这是正确的术语吗?)到一个结构中 - 结构的大小现在是 1484 字节(不确定这是否相关)

在这个例子中,数据是从Formula 1 2020 游戏发送的,奇怪的是m_carTelemetryData 数组(见下文)中的第一个条目总是很好,并且数据都是正确的。但是,在第一个之后的 22 中的每个条目都完全被 0 值、空值或结构中所有不同字段的完全古怪的值弄乱了。

我已经尝试了几种方法来查明问题,但现在我对我正在处理的事情的了解已经到了尽头。我最好的猜测是,将数据转换为结构时出现问题,或者发生了其他导致数据未对齐 (?) 的问题。

到目前为止我尝试了什么

将我的代码从“魔术编组”更改为使用 BinaryReader 逐字节手动读取数据 - 运气不好 使用 BitConverter.ToFoo(bytes, offset) 手动检查数据 - 不走运 Yolo 更改了 Pack 属性,假设那是我出错的地方 - 不走运 仔细检查了documentation 以确保我得到了正确的数据类型 - 我相当有信心我正确地“翻译”了它们 把我的头撞到墙上 - 仍然没有运气

我的问题:

我的代码是否有明显的问题,我根本看不到? 附带问题:我认为结构的大小应该与创建它的字节数组的大小相匹配的假设是错误的吗?

这是参考代码(如果缺少任何有用的信息,请告诉我):

包头

[StructLayout(LayoutKind.Sequential), Pack = 1]
struct PacketHeader2020 
    public ushort m_packetFormat;
    public byte m_gameMajorVersion;
    public byte m_gameMinorVersion;
    public byte m_packetVersion;
    public byte m_packetId;
    public ulong m_sessionUUID;
    public float m_sessionTime;
    public uint m_frameIdentifier;
    public byte m_playerCarIndex;
    public byte m_secondaryPlayerCarIndex;

数据包

public struct CarTelemetryPacket2020

    public PacketHeader2020 m_header;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
    public CarTelemetryData2020[] m_carTelemetryData;

    public ButtonFlag m_buttonStatus;
    public byte m_mfdPanelIndex;
    public byte  m_mfdPanelIndexSecondaryPlayer;
    public byte m_suggestedGear;
;

CarTelemetryData2020

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryData2020

    public ushort m_speed;
    public float m_throttle;
    public float m_steer;
    public float m_brake;
    public byte m_clutch;
    public sbyte m_gear;
    public ushort m_engineRPM;
    public byte m_drs;
    public byte m_revLightsPercent;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_brakesTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_tyresSurfaceTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_tyresInnerTemperature;
    public ushort m_engineTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public float[] m_tyresPressure;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public SurfaceType[] m_surfaceType;

byte[] -> 结构

public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct

  var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
  try
  
    return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
  
  finally
  
    handle.Free();
  

【问题讨论】:

【参考方案1】:

我刚刚将您的代码粘贴到LINQPad 并发现了一些问题。 CarTelemetryPacket2020 结构需要一个 pack 子句。 m_tyresSurfaceTemperaturem_tyresInnerTemperature 也应该是一个字节。以下根据协议规范返回 1307 的大小。你是对的,尺寸应该匹配,我没有看到你的代码有任何其他明显的问题。

void Main()

    System.Runtime.InteropServices.Marshal.SizeOf(typeof(CarTelemetryPacket2020)).Dump();


[StructLayout(LayoutKind.Sequential, Pack = 1) ]
public struct PacketHeader2020

    public ushort m_packetFormat;
    public byte m_gameMajorVersion;
    public byte m_gameMinorVersion;
    public byte m_packetVersion;
    public byte m_packetId;
    public ulong m_sessionUUID;
    public float m_sessionTime;
    public uint m_frameIdentifier;
    public byte m_playerCarIndex;
    public byte m_secondaryPlayerCarIndex;


// Added pack = 1
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryPacket2020

    public PacketHeader2020 m_header;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
    public CarTelemetryData2020[] m_carTelemetryData;

    public UInt32 m_buttonStatus;
    public byte m_mfdPanelIndex;
    public byte m_mfdPanelIndexSecondaryPlayer;
    public byte m_suggestedGear;
;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryData2020

    public ushort m_speed;
    public float m_throttle;
    public float m_steer;
    public float m_brake;
    public byte m_clutch;
    public sbyte m_gear;
    public ushort m_engineRPM;
    public byte m_drs;
    public byte m_revLightsPercent;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_brakesTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    // Changed following to byte
    public byte[] m_tyresSurfaceTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    // Changed following to byte
    public byte[] m_tyresInnerTemperature;
    public ushort m_engineTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public float[] m_tyresPressure;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] m_surfaceType;

【讨论】:

我不敢相信我没有看到这一点,我花了几天时间试图找出问题所在,但我只是没有看到。缺少的只是您指出的错误数据类型,我只是忘记在此处复制和粘贴 Pack 属性。非常感谢您的帮助!

以上是关于解析的字节数组在解析成结构后变成垃圾的主要内容,如果未能解决你的问题,请参考以下文章

C# 将字节解析为结构顺序

在 Java 中解析字节数组

在压缩数据字节数组的末尾添加一些垃圾字节后,是不是可以使用 GZIP 解压缩数据?

Java中的解析字节数组

将 C 字节数组解析为 Java ByteBuffer。

BitBlt 转换为字节数组并从 c++ 解析为 c#