将非常量大小的结构转换为字节数组

时间:2022-01-24 19:22:03

I'm working on a library that communicated with a µController via UDP-Messages. For that I'm using a custom protocol which is basically a struct consisting of 2 elements: The header (some metadata + checksum) and the payload. The communication is done via the System.Net.Sockets.UDPClient Class. To convert my data I'm using the following function:

我正在开发一个通过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();
}

I'm running into problems now if I want to send some payload which is not of a constant size, for example if I just want to write some data of variable length to the µController. One workaround that I am currently using is to just encapsulate my payload in a struct of a constant (maximum) size, but that seems not very efficient to me.

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

So, is there any way to convert a struct of a non constant-size to a Byte-Array with C#? For example this struct:

那么,有没有办法将非常量大小的结构转换为带有C#的字节数组?例如这个结构:

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

2 个解决方案

#1


1  

In your PERIPHERY__PROTOCOL example, you aren't actually putting the data into the struct - you are creating a separate array and putting the data there. You can still do that, absolutely - but you'll have to pay the heap overheads. In that case, forget about Marshal - you'd just pass arr to the struct.

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

But if you mean "can I have variable length structs in .NET"; no - no you cannot.

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

#2


1  

Why you want to use a struct?

为什么要使用结构?

Use a class. This sample code lets you add new packet types and convert them to bytes. If you want mechanism which will convert it back (from bytes to classes), you need to add your own invention.

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

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);
    }
}

#1


1  

In your PERIPHERY__PROTOCOL example, you aren't actually putting the data into the struct - you are creating a separate array and putting the data there. You can still do that, absolutely - but you'll have to pay the heap overheads. In that case, forget about Marshal - you'd just pass arr to the struct.

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

But if you mean "can I have variable length structs in .NET"; no - no you cannot.

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

#2


1  

Why you want to use a struct?

为什么要使用结构?

Use a class. This sample code lets you add new packet types and convert them to bytes. If you want mechanism which will convert it back (from bytes to classes), you need to add your own invention.

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

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);
    }
}