Проанализированный массив байтов превращается в мусор после анализа в структуру

#c# #arrays #struct #udp

#c# #массивы #структура #udp

Вопрос:

Я получаю пакеты данных через UDP, которые я разбираю на структуры, и в определенной ситуации все мои данные перепутываются, и я не могу понять, в чем может быть проблема. Позвольте мне дать вам обзор того, что я делаю.

  1. UDP-приемник получает пакет данных и передает его на обратный вызов — размер массива байтов на этом этапе составляет 1307 байт, что является ожидаемым размером пакета
  2. Массив байтов сортируется (это правильный термин?) В структуру — размер структуры теперь составляет 1484 байта (не уверен, что это актуально)

В этом примере данные отправляются из игры Formula 1 2020, и любопытно, что первая запись в m_carTelemetryData массиве (см. Ниже) всегда в порядке, и все данные в порядке. Однако каждая запись из 22 после первой полностью перепутана со значениями 0, нулевыми значениями или совершенно диковинными значениями для всех разных полей в структуре.

Я уже попробовал несколько вещей, чтобы точно определить проблему, но теперь я дошел до конца своих знаний о том, с чем я здесь имею дело. Мое лучшее предположение заключается в том, что что-то идет не так при преобразовании данных в структуру или происходит что-то еще, что вызывает рассогласование (?) Данных.

Что я пробовал до сих пор

  • Изменил мой код с «волшебной сортировки» на ручное чтение данных по байтам с использованием BinaryReader — не повезло
  • Вручную проверил данные с помощью BitConverter.ToFoo(bytes, offset) — не повезло
  • Yolo изменил Pack атрибут, предполагая, что я ошибся — не повезло
  • Дважды проверил документацию, чтобы убедиться, что я правильно определил типы данных — я вполне уверен, что я «перевел» их правильно
  • Ударился головой о стену — все еще не повезло

Мои вопросы:

  • Есть ли что-то очевидное неправильное в моем коде, которое я просто не вижу?
  • Побочный вопрос: ошибаюсь ли я в своем предположении, что размер структуры должен соответствовать размеру массива байтов, из которого он был создан?

Вот код для справки (если чего-то полезного не хватает, пожалуйста, дайте мне знать):

Заголовок пакета

 [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[] -> struct

 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_tyresSurfaceTemperature и m_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;
}
  

Комментарии:

1. Я не могу поверить, что я этого не видел, я потратил дни, пытаясь выяснить, что было не так, и я просто этого не видел. Не хватало только типов данных wroing, на которые вы указали, я только забыл скопировать и вставить атрибут Pack здесь. Большое спасибо за помощь!