C#Networking - 使用StructLayout pack 1发送struct缓冲区

时间:2013-11-15 18:00:57

标签: c# sockets tcp struct marshalling

我有一个由我自己编写的服务器客户端程序。我使用TCP协议的套接字。我正在尝试从客户端向服务器发送缓冲区。该缓冲区是struct,我将其转换为字节数组发送它,在服务器上我将它从字节数组转换回struct:

    public static byte[] StructToByteArray(loginStruct obj)
    {
        int len = Marshal.SizeOf(typeof(loginStruct));
        byte[] arr = new byte[len];

        IntPtr ptr = Marshal.AllocHGlobal(len);
        Marshal.StructureToPtr(obj, ptr, true);
        Marshal.Copy(ptr, arr, 0, len);

        Marshal.FreeHGlobal(ptr);
        return arr;

    }
    public static void ByteArrayToStruct(byte[] buffer, ref loginStruct obj)
    {
        int len = Marshal.SizeOf(typeof(loginStruct));

        IntPtr i = Marshal.AllocHGlobal(len);
        Marshal.Copy(buffer, 0, i, len);
        obj = (loginStruct)Marshal.PtrToStructure(i, obj.GetType());

        Marshal.FreeHGlobal(i);
    }

我的结构:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct loginStruct
{
    public string userName;
    public string password;

    public loginStruct(string userName, string password)
    {
        this.userName = userName;
        this.password = password;
    }
}

我在线上遇到了运行时错误:

obj = (loginStruct)Marshal.PtrToStructure(i, obj.GetType());

错误是:

  

尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

在ByteArrayToStruct函数的服务器上,len等于缓冲区大小为16。

1 个答案:

答案 0 :(得分:1)

如果您尝试发送固定大小的char *缓冲区,则字符串的默认编组可能不适用于您。您应该使用MarshalAs属性标记字符串并指定实际的缓冲区大小,以便.NET知道在非托管版本的结构中分配和复制的内存量。它看起来像这样:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct loginStruct
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
    public string userName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]
    public string password;

    public loginStruct(string userName, string password)
    {
        this.userName = userName;
        this.password = password;
    }
}

有关如何跨托管/非托管边界编组字符串的详细信息,请参阅:Default Marshaling for Strings,尤其是“结构中使用的字符串”部分。

顺便说一下,我认为您不需要指定Pack,因为您的数组每个都是8个字节,所以没有填充空间。您需要非常确定需要覆盖结构上的打包,尤其是如果您的代码需要在32位和64位系统上工作。如果C定义不包含#pragma pack或类似内容,那么您可能也不应该这样做。