对于非托管C+,序列化/编组C#中的简单对象以通过网络发送+;申请阅读 我正在编写一个C应用程序,它需要使用预先定义的消息在网络上与非托管C++应用程序通信。每条消息都以消息id和长度开头 Byte 0 MessageId Byte 1 Length

对于非托管C+,序列化/编组C#中的简单对象以通过网络发送+;申请阅读 我正在编写一个C应用程序,它需要使用预先定义的消息在网络上与非托管C++应用程序通信。每条消息都以消息id和长度开头 Byte 0 MessageId Byte 1 Length,c#,c++,c,serialization,marshalling,C#,C++,C,Serialization,Marshalling,之后,每封邮件的内容都会有所不同。如设置消息的时间,将 Byte 0 MessageId Byte 1 Length Byte 2..5 Time 首先,我想我应该创建一个基类,所有其他消息都将从该基类继承,该基类将有一个方法来“序列化”它: 在C++代码中,如果我创建了一个 SetTimeMeSe>/Cuth>并想通过网络发送,那么在基类上定义了一个方法,比如“代码> GetasMeals//Cuff>”,它将简单地将对象的内容复制到一个缓冲区中。这适用于所有派生类型

之后,每封邮件的内容都会有所不同。如设置消息的时间,将

Byte 0      MessageId
Byte 1      Length
Byte 2..5   Time
首先,我想我应该创建一个基类,所有其他消息都将从该基类继承,该基类将有一个方法来“序列化”它:

在C++代码中,如果我创建了一个<代码> SetTimeMeSe>/Cuth>并想通过网络发送,那么在基类上定义了一个方法,比如“代码> GetasMeals//Cuff>”,它将简单地将对象的内容复制到一个缓冲区中。这适用于所有派生类型的

BaseMessage

我怎样才能在C#中做类似的事情?我尝试使用
[Serializable]
属性和
二进制格式化程序
,但它返回了一个巨大的字节数组,而不仅仅是值实际包含的6个字节

我还研究了
编组
,但它似乎只适用于结构?如果是这样的话,似乎每个消息类型都需要做很多工作,因为结构不支持继承(我需要实现很多消息)

我还将接收相同格式的消息,这些消息需要反序列化回一个对象。有谁知道一个简单的方法来实现我的目标

编辑

在从Spo1ler输入之后,我对结构和编组做了一些实验

通过这种方式,将任何消息序列化到字节数组非常简单

public interface IMsg
{
    byte MessageId { get; }
    byte Length { get; }
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SetTime: IMsg
{
    public byte Id { get; }
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] Time;

    public StartLoggerWithSampleInterval(byte time)
    {
        Id = (byte) MessageType.SetTime;
        Time = time;
    }
}
然后我使用这个静态类来序列化消息:

public static class MessageSerializer
{
    public static byte[] Serialize(IMsg msg)
    {
        int size = Marshal.SizeOf(msg);
        byte[] serialized = new byte[size];
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(msg, ptr, true);
        Marshal.Copy(ptr, serialized, 0, size);
        Marshal.FreeHGlobal(ptr);

        return serialized;
    }
}
起初,这似乎是我想要的工作。这样做有什么问题吗


然而,消息的反序列化将是一个痛苦的写入过程,尽管消息中的第一个字节(MessageId)应该告诉我字节数组转换为什么类型的结构,它保存类型来自哪个程序集,等等,因此在您的情况下,当消息的字节结构已知时,它没有用处

要使结构像普通的旧数据一样成功序列化,需要在结构上使用
StructLayoutAttribute
,如下所示

[StructLayout(LayoutKind.Explicit)]
public class Message
{
    [FieldOffset(0)] public byte MessageId;
    [FieldOffset(1)] public byte Length;
    [FieldOffset(2)] public byte[] Data;
}
byte[] GetBytes(Message message) {
    int size = Marshal.SizeOf(message);
    byte[] arr = new byte[size];
    IntPtr ptr = Marshal.AllocHGlobal(size);

    Marshal.StructureToPtr(message, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);

    return arr;
}
Message FromBytes(byte[] bytes) {
    Message message = new Message();

    int size = Marshal.SizeOf(message);
    IntPtr ptr = Marshal.AllocHGlobal(size);

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

    message = (Message)Marshal.PtrToStructure(ptr, message.GetType());
    Marshal.FreeHGlobal(ptr);

    return message;
}
StructLayoutAttribute
和任何其他参数,然后
LayoutKind.Auto
告诉编译器不要更改内存中结构的布局,而是使用您提供的布局。在这种情况下,
Explicit
layout,您可以在其中手动告知字段的偏移量

您也应该考虑更改消息格式,因为在机器性能方面,<>代码> FieldOffice s在性能方面可能是相当昂贵的,因为计算机与机器字大小对齐的数据工作得更好,而不是任意数量的字节。 以这种方式尝试使用基类构建层次结构是一种不好的方法,如果您确实希望在消息类型中有层次结构,那么最好使用接口,但仍然需要正确地布局数据

然后,您可以使用封送处理将数据序列化为
字节
数组,如下所示

[StructLayout(LayoutKind.Explicit)]
public class Message
{
    [FieldOffset(0)] public byte MessageId;
    [FieldOffset(1)] public byte Length;
    [FieldOffset(2)] public byte[] Data;
}
byte[] GetBytes(Message message) {
    int size = Marshal.SizeOf(message);
    byte[] arr = new byte[size];
    IntPtr ptr = Marshal.AllocHGlobal(size);

    Marshal.StructureToPtr(message, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);

    return arr;
}
Message FromBytes(byte[] bytes) {
    Message message = new Message();

    int size = Marshal.SizeOf(message);
    IntPtr ptr = Marshal.AllocHGlobal(size);

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

    message = (Message)Marshal.PtrToStructure(ptr, message.GetType());
    Marshal.FreeHGlobal(ptr);

    return message;
}
如果你想反序列化,那么你需要这样一个方法

[StructLayout(LayoutKind.Explicit)]
public class Message
{
    [FieldOffset(0)] public byte MessageId;
    [FieldOffset(1)] public byte Length;
    [FieldOffset(2)] public byte[] Data;
}
byte[] GetBytes(Message message) {
    int size = Marshal.SizeOf(message);
    byte[] arr = new byte[size];
    IntPtr ptr = Marshal.AllocHGlobal(size);

    Marshal.StructureToPtr(message, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);

    return arr;
}
Message FromBytes(byte[] bytes) {
    Message message = new Message();

    int size = Marshal.SizeOf(message);
    IntPtr ptr = Marshal.AllocHGlobal(size);

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

    message = (Message)Marshal.PtrToStructure(ptr, message.GetType());
    Marshal.FreeHGlobal(ptr);

    return message;
}
如果您需要根据收到的消息在运行时决定使用哪个类,那么这些方法中的每一个都会变得相当大,并且对您的层次结构非常具体,但是您当然可以通过查看第一个字节,然后决定如何相应地反序列化它们来完成


但在我看来,尝试基于消息的某些公共部分进行层次结构是一种糟糕的方法。你宁愿做一些已知类型的消息序列来区分下一条消息或类似的消息,但这取决于你。我在这里提供的信息应该足以将任何整数类型序列化为字节数组,以后可以通过网络发送。

也许这会有所帮助:这个答案与他的问题无关……这根本不是答案,因为他想从网络包中反序列化类感谢答案。如果这些结构都实现了相同的接口,那么它们能否以通用方式序列化为字节数组?因此,我没有在每个结构和单个结构上使用GetBytes和FromBytes,而是使用了一个静态函数,它可以使用一个实现IMessage的结构?当然,可以,但不能采用上述方式。您需要手动构造数组,因为.NET序列化不适合以一般方式执行此类任务。这是一项艰巨的任务,我确信它适用于堆栈溢出回答,但我可以为您提供一些关于如何实现它的一般指导。Fwiw,使用Pack=1足以避免使用[FieldOffset]。然而,C++代码使用它的可能性不大,它默认为PACK=8。让它匹配是很重要的。不使用Protobuf之类的东西的危险。是的,这个协议很早以前就定义好了,在消息中保存字节过去和现在都比使用易于使用的东西更重要。但愿我能用protobuf:)