使用 Protobuf 和内存映射文件 C# 的 IPC

Posted

技术标签:

【中文标题】使用 Protobuf 和内存映射文件 C# 的 IPC【英文标题】:IPC Using Protobuf and Memory Mapped Files C# 【发布时间】:2016-03-30 17:20:27 【问题描述】:

我正在编写一个项目来将对象从父应用程序传递到子应用程序。我正在使用 Protobuf 序列化和反序列化数据。我还使用非持久内存映射文件在序列化时写入(并在反序列化时读取)。代码如下:

[ProtoContract(SkipConstructor = true)]
public class Test

    [ProtoMember(1)]
    public int ID  get; private set; 
    [ProtoMember(2)]
    public bool TestBool  get; private set; 
    [ProtoMember(3)]
    public string MessageBody  get; private set; 

    public Test(int id, bool testBool, string messageBody)
    
        this.ID = id;
        this.TestBool = testBool;
        this.MessageBody = messageBody;
    


    public void Serialize()
    
        MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 1000);
        using (MemoryMappedViewStream stream = mmf.CreateViewStream())
        
            Serializer.SerializeWithLengthPrefix(stream, this, PrefixStyle.Base128);
            stream.Flush();
        
    

    public static Test Deserialize()
    
        using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
        
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            
                return Serializer.DeserializeWithLengthPrefix<Test>(stream, PrefixStyle.Base128);
            
        
    


//On the parent application
var msg = new SharedLibrary.Test(1234, true, "Test message hahah");
msg.Serialize();
//spawn child application



//On the child application
Test result = Test.Deserialize();   

当我运行此代码时,我收到以下错误(调用 Serializer.Deserialize 时):

抛出异常:protobuf-net.dll 中的“ProtoBuf.ProtoException” 附加信息:源数据中的无效字段:0 我认为发生此错误是因为流大于它包含的实际数据。当流被反序列化时,我认为它开始读取超出实际数据的内容。

抛出异常:protobuf-net.dll 中的“ProtoBuf.ProtoException” 附加信息:没有为 Test 找到无参数构造函数

我不确定解决此问题的最佳方法。有没有办法从流中读取字节,直到没有数据留下然后停止?如果不能,我可以用另一种方式解决这个问题吗?

【问题讨论】:

一个疯狂的猜测,可能是因为属性set是私有的?使用Attributes 可能表明使用了反射。 为了安全起见,我试过了,但还是抛出了同样的错误。我很确定 Protobuf 只是调用构造函数,所以它不应该是一个问题 其实我刚刚发现了一些东西。我还有一个默认构造函数 public Test() 。当我删除它时,抛出一个新异常: 异常抛出:protobuf-net.dll 中的'ProtoBuf.ProtoException' 附加信息:没有为测试找到无参数构造函数......所以看起来反序列化正在接收的流是空的?不知道为什么会发生这种情况。 【参考方案1】:

我不确定解决此问题的最佳方法

    添加一个无参数构造函数(如果你愿意,可以是private),或者 将SkipConstructor = true 作为参数添加到您的类型上的[ProtoContract(...)]

然而。有没有办法从流中读取字节,直到没有数据留下然后停止?

是的,这是协议缓冲区中的默认设置,因为最外面的消息不包括长度标记或结束标记(它被设计为可附加的)。然而,在你的情况,这可能不是你想要的,因为会有各种垃圾(可能全为零,也可能不是)之后您序列化的数据。您可能想改用SerializeWithLengthPrefixDeserializeWithLengthPrefix。如果您的数据只有 1000 字节,MemoryStream 就可以了 - 无需使用非托管内存。

【讨论】:

我编辑了代码以反映您的更改。关于非托管内存的最后一部分.. 你是说在我的情况下我应该只使用 MemoryStream 而不是 MemoryMappedFile? @user2481095 我对您的具体用例了解不多,无法肯定地说,但是,我不会跳过 使用非托管内存。如果有充分的理由:太好了

以上是关于使用 Protobuf 和内存映射文件 C# 的 IPC的主要内容,如果未能解决你的问题,请参考以下文章

C#大文件读取和查询--内存映射

在 C# 中使用内存映射文件时是不是可以避免数据副本?

C#大文件读取和查询--内存映射

内存映射文件 VS 命名管道 - C#

C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped

C# OutOfMemory、映射内存文件或临时数据库