【问题标题】:Convert structs of non-constant size to byte-array将非常量大小的结构转换为字节数组
【发布时间】:2017-05-18 13:29:16
【问题描述】:

我正在开发一个通过UDP-MessagesµController 通信的库。为此,我使用了一个自定义协议,它基本上是一个由 2 个元素组成的结构:标头(一些元数据 + 校验和)和有效负载。 通信是通过System.Net.Sockets.UDPClient 类完成的。要转换我的数据,我使用以下函数:

private List<byte> GetBytes(object str)
{
    int size = Marshal.SizeOf(str);
    byte[] arr = new byte[size];
    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.StructureToPtr(str, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);
    return arr.ToList();
}

如果我想发送一些不是恒定大小的有效负载,例如,如果我只想将一些可变长度的数据写入 µController,我现在遇到了问题。我目前使用的一种解决方法是将我的有效负载封装在一个恒定(最大)大小的结构中,但这对我来说似乎不是很有效。

那么,有没有什么方法可以用 C# 将非恒定大小的结构转换为字节数组?例如这个结构:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct PERIPHERY__PROTOCOL
{
    public PERIPHERY_HEADER strHeader;       
    public byte[] Data;
}

【问题讨论】:

  • @KillerIsDead 我认为这并不能解决这里问题的关键部分
  • 请注意,在您的代码中,您正在分配一些非托管内存,写入它,然后释放非托管内存 - 这...实际上对任何事情都不是很有用

标签: c# arrays struct marshalling data-conversion


【解决方案1】:

在您的PERIPHERY__PROTOCOL 示例中,您实际上并没有将数据放入结构中- 您正在创建一个单独的 数组并将数据放在那里。你仍然可以这样做,绝对 - 但你必须支付堆开销。在这种情况下,忘记Marshal - 你只需传递 arr 到结构

但是,如果您的意思是“我可以在 .NET 中使用可变长度的结构吗”;不 - 不,你不能。

【讨论】:

    【解决方案2】:

    为什么要使用结构体?

    使用类。此示例代码允许您添加新的数据包类型并将它们转换为字节。如果你想要将它转换回来(从字节到类)的机制,你需要添加你自己的发明。

    public abstract class Packet
    {
        public int PacketType { get; }
    
        public Packet (int packetType)
        {
            PacketType = packetType;
        }
    
        protected abstract byte[] GetPayload ();
    
        private int CalculateChecksum ()
        {
            byte[] packetTypeBytes = BitConverter.GetBytes (PacketType);
            byte[] payloadBytes    = GetPayload ();
            byte[] lengthBytes     = BitConverter.GetBytes (payloadBytes.Length);
    
            return 0; // add some logic to calculate checksum from all bytes
        }
    
        public byte[] ToBytes ()
        {
            byte[] packetTypeBytes = BitConverter.GetBytes (PacketType);
            byte[] checksumBytes   = BitConverter.GetBytes (CalculateChecksum ());
            byte[] payloadBytes    = GetPayload ();
            byte[] lengthBytes     = BitConverter.GetBytes (payloadBytes.Length);
    
            return packetTypeBytes.Concat (lengthBytes).Concat (checksumBytes).Concat (payloadBytes).ToArray ();
    
        }
    }
    
    
    
    public sealed class ActionA : Packet
    {
        public string Message { get; }
    
        public ActionA (string message) : base (0)
        {
            Message = message;
        }
    
        protected override byte[] GetPayload ()
        {
            return Encoding.ASCII.GetBytes (Message);
        }
    
    
    }
    
    
    
    public sealed class ActionB : Packet
    {
        public int Value { get; }
    
        public ActionB (int value) : base (1)
        {
            Value = value;
        }
    
        protected override byte[] GetPayload ()
        {
            return BitConverter.GetBytes (Value);
        }
    }
    

    【讨论】:

    • 在 µController 上运行的 C 代码使用结构。我只是将它们复制并改编到我的 C# 库中
    • @kain:忘记控制器端的 C 代码。只需发送有效数据。如果您使用 C#,则像 C# 中的程序,而不是 C 中的程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-11
    • 2013-01-21
    • 2011-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多