C#包1结构布局网络

C#包1结构布局网络,c#,networking,marshalling,structlayout,C#,Networking,Marshalling,Structlayout,我试图从服务器向我自己制作的客户端发送一个缓冲区。它与TCP上的套接字一起工作 我需要发送一个结构: [StructLayout(LayoutKind.Sequential, Pack = 1)] struct loginStruct { public string userName; public string password; public loginStruct(string userName, string password) { t

我试图从服务器向我自己制作的客户端发送一个缓冲区。它与TCP上的套接字一起工作

我需要发送一个结构:

[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;
    }
}
我得到了从字节数组到结构以及从结构到字节数组的转换函数:

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

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

        Marshal.FreeHGlobal(ptr);
        return arr;

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

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

        Marshal.FreeHGlobal(i);
    }

在客户端中,我收到了缓冲区,但当客户端尝试使用ByteArrayToStruct函数时,我遇到了一个运行时错误。

好的,在尝试轻松解析来自专有服务器的响应时,我想到了同样的事情。下面是一个根据您的具体情况调整的简化示例

首先,您需要一些扩展来简化整个过程。请注意,要做到这一点,您需要使用.NET3.5或更高版本,或者查看答案

现在,以下是我为扩展类所做的工作:

public static class EndianExtensions {
    /// <summary>
    /// Convert the bytes to a structure in host-endian format (little-endian on PCs).
    /// To use with big-endian data, reverse all of the data bytes and create a struct that is in the reverse order of the data.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="buffer">The buffer.</param>
    /// <returns></returns>
    public static T ToStructureHostEndian<T>(this byte[] buffer) where T : struct {
        GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        T stuff = (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        handle.Free();
        return stuff;
    }

    /// <summary>
    /// Converts the struct to a byte array in the endianness of this machine.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="structure">The structure.</param>
    /// <returns></returns>
    public static byte[] ToBytesHostEndian<T>(this T structure) where T : struct {
        int size = Marshal.SizeOf(structure);
        var buffer = new byte[size];
        GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        Marshal.StructureToPtr(structure, handle.AddrOfPinnedObject(), true);
        handle.Free();
        return buffer;
    }

    public static Dictionary<string, string> GetTypeNames<T>(this T structure) where T : struct {
        var properties = typeof(T).GetFields();

        var dict = new Dictionary<string, string>();

        foreach (var fieldInfo in properties) {
            string[] words = fieldInfo.Name.Split('_');
            string friendlyName = words.Aggregate(string.Empty, (current, word) => current + string.Format("{0} ", word));
            friendlyName = friendlyName.TrimEnd(' ');
            dict.Add(fieldInfo.Name, friendlyName);
        }
        return dict;
    }
}
现在,上面假设发送和接收数据缓冲区的实际内容定义不同,因此在您的情况下,只需定义其中一个结构。请注意,它们是按相反顺序指定的;同样,这是因为我需要用big-endian格式传输它

现在只需创建要发送的结构:

// buffer for storing our received bytes
var barBuf = new byte[64];

// struct that we're sending
var fuz = new Foo {
    User_Name = "username",
    Password = "password"
};

// get the byte equivalent of fuz
var fuzBytes = fuz.ToBytesHostEndian().Reverse().ToArray();

// simulates sock.send() and sock.receive()
// note that this does NOT simulate receiving big-endian data!!
fuzBytes.CopyTo(barBuf, 0);

// do the conversion from bytes to struct
barBuf = barBuf.Reverse().ToArray();
需要注意的是,通过使用
CopyTo()
模拟
sock.Send()
sock.Receive()
命令,它不会在
barBuf
中生成大端数组。我已经相应地修改了代码,但如果您确实使用它来接收big-endian数据,只需更改代码中指示的行即可


我希望这有帮助。由于这些信息分散在多个来源中,我花了很多时间才弄清楚自己的想法。

如何发送数据?您可以通过套接字发布用于发送/接收的代码吗?您发布的内容似乎可以工作,错误可能是由错误的传输引起的。运行时异常是什么?我不会在sizeof调用或PtrToStructure调用中使用“obj”,而是指结构本身。obj引用可能为null,因为它的类型为“object”,而不是“loginStruct”。还有,你为什么不使用任何序列化程序,而这样做是“艰难的”呢?如果二进制格式是给定的,我仍然建议在编组(编码/安全性)speedbump上使用带有memorystream的BinaryWriter/BinaryReader。
// buffer for storing our received bytes
var barBuf = new byte[64];

// struct that we're sending
var fuz = new Foo {
    User_Name = "username",
    Password = "password"
};

// get the byte equivalent of fuz
var fuzBytes = fuz.ToBytesHostEndian().Reverse().ToArray();

// simulates sock.send() and sock.receive()
// note that this does NOT simulate receiving big-endian data!!
fuzBytes.CopyTo(barBuf, 0);

// do the conversion from bytes to struct
barBuf = barBuf.Reverse().ToArray();
// change this to ToStructureHostEndian<Bar>() if receiving big endian
var baz = barBuf.ToStructureHostEndian<Foo>();
// get the property names, friendly and non-friendly
var bazDict = baz.GetTypeNames();

// change this to typeof(Bar) if receiving big endian
var bazProps = typeof(Foo).GetFields();

// loop through the properties array
foreach (var fieldInfo in bazProps) {
    var propName = fieldInfo.Name;
    // get the friendly name and value
    var fieldName = bazDict[propName];
    var value = fieldInfo.GetValue(baz);

    // do what you want with the values
    Console.WriteLine("{0,-15}:{1,10}", fieldName, value);
}