用结构数组和大小参数索引封送C#结构

时间:2019-03-10 23:55:17

标签: c# struct tcp marshalling packet

我已经阅读了几个有关的主题,但是我仍然无法理解无法轻松地将此结构转换为字节数组的真正限制:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct B {
  public int b_a;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct A {
  public int sizeB;

  [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
  public B[] b;
}

我正在编写一个TCP通信程序,因此我想将我的S2C数据包构建为一个结构,然后将其作为byte []发送,因此我正在寻找实现这一目标的最便宜,最快的方法。

我已经以多种方式尝试过Marsheling,但是Marshal.SizeOf()中总是存在一些例外。

在此示例中,我收到以下错误:“ [...]无法作为非托管结构进行封送;无法计算出有意义的大小或偏移量。”

结构初始化,例如:

A a = new A();
B[] b = new B[5];

a.sizeB = 5;
a.b = b;

Marshal.SizeOf(a);

1 个答案:

答案 0 :(得分:0)

您没有像C或C ++一样具有用于低级内存访问的控件。当C#中的数组长度不确定时,您需要做一些手工工作。

以下是完成此操作的几种方法。

struct B
{
    public int b_a;
}

struct A
{
    public int sizeB;

    public B[] b;
}

第一个是BinaryWriter。如果您的结构没有很多字段,则可以更快。

static byte[] ConvertToByte(A a)
{
    using (var ms = new MemoryStream())
    using (var writer = new BinaryWriter(ms))
    {
        writer.Write(a.sizeB);

        foreach (var b in a.b)
            writer.Write(b.b_a);

        return ms.ToArray();
    }
}

另一个可以像以前一样使用编组,但是显式地遍历数组。

static byte[] ConvertToByte(A a)
{
    var bStructSize = Marshal.SizeOf<B>();
    var size = bStructSize * a.b.Length;

    var arr = new byte[size + 4];

    var ptr = Marshal.AllocHGlobal(size);

    for (int i = 0; i < a.b.Length; i++)
        Marshal.StructureToPtr(a.b[i], ptr + i * bStructSize, true);

    Marshal.Copy(ptr, arr, 4, size);

    Array.Copy(BitConverter.GetBytes(a.sizeB), arr, 4);

    return arr;
}