使用Protobuf和内存映射文件在C#中实现IPC

3

我正在编写一个项目,将一个对象从父应用程序传递到子应用程序。我使用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函数会出现以下错误: Exception thrown: 'ProtoBuf.ProtoException' in protobuf-net.dll Additional information: Invalid field in source data: 0 我认为这个错误是因为流比实际数据要大而导致的。当反序列化流时,我认为它开始读取超出实际数据范围的内容。 同时,还有另一个错误: Exception thrown: 'ProtoBuf.ProtoException' in protobuf-net.dll Additional information: No parameterless constructor found for Test 我不确定最好的解决方法是什么。是否有一种读取流字节直到没有数据剩余并停止读取的方法?如果没有,我可以用其他方法解决吗?

一个猜测,可能是因为set属性是私有的?使用Attributes可能表明使用了反射。 - Jeroen van Langen
我试过了,只是为了安全起见,但是仍然出现了相同的错误。我非常确定 Protobuf 只是调用构造函数,所以这不应该是问题。 - user2481095
实际上,我刚刚发现了一些事情。 我也有一个默认构造函数“public Test() { }”。当我将其删除时,会抛出一个新的异常:“Exception thrown:'ProtoBuf.ProtoException' in protobuf-net.dll”。 附加信息:“未找到Test的无参数构造函数...”,因此看来Deserialize接收的流为空?不确定为什么会发生这种情况。 - user2481095
1个回答

3

我不确定最好的解决方法

  1. 增加一个无参构造函数(如果喜欢,可以将其设为private),或者
  2. 在类型上的[ProtoContract(...) ]添加SkipConstructor = true参数

然而。有没有一种方法可以从流中读取字节直到没有数据然后停止?

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


我已经编辑了代码以反映您的更改。关于未受管理的内存的最后一部分...您是说在我的情况下,我应该只使用MemoryStream而不是MemoryMappedFile吗? - user2481095
@user2481095 我对于你具体的使用情况不是很了解,所以不能确定地说,但是我不会轻率地使用非托管内存。如果有充分的理由:那太好了。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接