我正在尝试使用内存映射文件将C#对象从一个进程发送到另一个进程,并且试图使用BinaryFormatter或protobuf-net。都不起作用-显然是因为我必须使用固定长度的字节数组,而protobuf-net需要一个长度正确的数组?
使用protobuf-net,我在Deserialize上收到此异常:“ ProtoException:'缓冲区中剩余未使用的数据;这表明输入已损坏' 在该行:“ message1 = Serializer.Deserialize(memoryStream);
这是我的代码。在这一点上,我只是尝试一个简单的示例,以使其在基本级别上起作用: 这是我要在程序之间发送的对象:
[ProtoContract]
public class IpcMessage
{
public IpcMessage() { }
[ProtoMember(1)]
public string title { get; set; }
[ProtoMember( 2 )]
public string content { get; set; }
}
这是发送IpcMessage对象的(简化-我删除了同步)代码:
static void SampleSend()
{
// Create the memory-mapped file which allows 'Reading' and 'Writing'
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen( "MyMmfName", 1024, MemoryMappedFileAccess.ReadWrite ))
{
// Create a view-stream for this process, which allows us to write data from offset 0 to 1024 (whole memory)
using (MemoryMappedViewStream mmvStream = mmf.CreateViewStream( 0, 1024))
{
IpcMessage message1 = new IpcMessage();
message1.title = "test";
message1.content = "hello world";
Serializer.Serialize( mmvStream, message1 );
}
}
}
这是接收程序中的代码(简化了一点):
// Create the memory mapped file..
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen( "MyMmfName", 1024, MemoryMappedFileAccess.ReadWrite ))
{
using (MemoryMappedViewAccessor mmvStream = mmf.CreateViewAccessor( 0, 1024, MemoryMappedFileAccess.Read ))
{
byte[] buffer = new byte[1024];
IpcMessage message1;
int numberBytesRead = mmvStream.ReadArray<byte>( 0, buffer, 0, 1024 );
var memoryStream = new MemoryStream(buffer);
// It is at this next line that I get ProtoException: 'Unconsumed data left in the buffer; this suggests corrupt input'
message1 = Serializer.Deserialize<IpcMessage>( memoryStream );
}
}
当我尝试使用BinaryFormatter时,它也抱怨。显然,我所做的事情基本上是错误的。
通过其他问题,我发现大多数实现似乎都有一个分配给正确长度的字节数组。在这里,我不知道提前的长度-它只是一个固定长度的1024字节数组(我暂时选择了这个大小)。还是很明显我很想念?
这是我第一次使用内存映射文件或protobuf-net。感谢您提供任何帮助或建议-谢谢您。
注意:我正在使用Visual Studio 2017 Enterprise 15.9.6,并且此代码针对.NET Framework 4.0
答案 0 :(得分:4)
选项1:告诉MemoryStream
在可选的构造函数重载中使用的正确字节数;这将适用于所有序列化器。
选项2,专门用于protobuf-net:使用ProtoReader
; Deserialize
API接受Stream
或 ProtoReader
;后者可以按名义上的长度构建,不会被过度阅读
方法3,同样是protobuf-net:使用*WithLengthPrefix
API进行序列化和反序列化
请注意,任何选项均假定您的代码可以可靠地确定长度,通常应使用所使用的“成帧”方法分别进行通信。选项3在内部处理此问题,但是在许多情况下,您自己的代码仍需要具有帧感知功能,这样您就不会消耗过多的代码并将我读到下一条消息(除非这是UDP,每个数据包恰好有一个帧,但是...并非如此,因为MemoryMappedFile
)。您还需要考虑如果有效负载大于预期的缓冲区大小会发生什么情况。
“成帧”的最简单方法是在每个逻辑块的开始处保留4个字节。从偏移量4开始进行序列化,然后在选择的字节顺序(通常为“小”)中使用固定的4字节布局,写回以偏移量0-thru-3写入的字节数。读取时:读取前4个字节以获取长度,然后从该段中消耗那么多字节。本质上,这是*WithLengthPrefix
内部所做的事情,只是支持一些不同的布局选项。