Reputation: 121
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:
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.
So, is there any way to convert a struct of a non constant-size to a Byte-Array with C#? For example this struct:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct PERIPHERY__PROTOCOL
{
public PERIPHERY_HEADER strHeader;
public byte[] Data;
}
Upvotes: 0
Views: 236
Reputation: 5884
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);
}
}
Upvotes: 1
Reputation: 1062560
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.
But if you mean "can I have variable length structs in .NET"; no - no you cannot.
Upvotes: 1