您如何知道要使用 protobuf-net 反序列化的字节数组的长度?
How can you know the length of the byte-array to deserialize using protobuf-net?
我正在尝试使用内存映射文件将 C# 对象从一个进程发送到另一个进程,并且我正在尝试使用 BinaryFormatter 或 protobuf-net。两者都不起作用 - 显然是因为我必须使用固定长度的字节数组,而 protobuf-net 需要一个长度完全正确的数组?
使用 protobuf-net,我在反序列化时得到这个异常:“ProtoException:'Unconsumed data left in the buffer; this suggests corrupt input'
在该行:“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
选项 1:告诉 MemoryStream
在可选构造函数重载中使用的正确字节数;这将适用于所有序列化程序。
选项2,专门针对protobuf-net:使用ProtoReader
; Deserialize
API 接受一个 Stream
或 一个 ProtoReader
;后者可以用名义长度构造,不会过度阅读
选项 3,同样是 protobuf-net:使用 *WithLengthPrefix
APIs 进行序列化和反序列化
请注意,任何选项都假定您的代码可以稳健地确定长度,通常应该在您使用的任何 "framing" 方法中单独传达。选项 3 在内部处理这个问题,但在许多情况下,您自己的代码仍然需要帧感知,这样您就不会消耗太多并阅读下一条消息(除非这是 UDP,每个数据包只有一帧,但是...不是,因为 MemoryMappedFile
)。您还需要考虑如果有效载荷大于预期缓冲区大小会发生什么情况。
"framing"最简单的方法是在每个逻辑块的开头保留 4 个字节;从偏移量 4 开始序列化,然后在偏移量 0 到 3 处写回写回的字节数,在您选择的字节顺序中使用固定的 4 字节布局(通常为 "little")。读取时:读取前 4 个字节以获得长度,然后从段中消耗那么多字节。这基本上就是 *WithLengthPrefix
内部所做的,除了支持一些不同的布局选项。
我正在尝试使用内存映射文件将 C# 对象从一个进程发送到另一个进程,并且我正在尝试使用 BinaryFormatter 或 protobuf-net。两者都不起作用 - 显然是因为我必须使用固定长度的字节数组,而 protobuf-net 需要一个长度完全正确的数组?
使用 protobuf-net,我在反序列化时得到这个异常:“ProtoException:'Unconsumed data left in the buffer; this suggests corrupt input' 在该行:“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
选项 1:告诉 MemoryStream
在可选构造函数重载中使用的正确字节数;这将适用于所有序列化程序。
选项2,专门针对protobuf-net:使用ProtoReader
; Deserialize
API 接受一个 Stream
或 一个 ProtoReader
;后者可以用名义长度构造,不会过度阅读
选项 3,同样是 protobuf-net:使用 *WithLengthPrefix
APIs 进行序列化和反序列化
请注意,任何选项都假定您的代码可以稳健地确定长度,通常应该在您使用的任何 "framing" 方法中单独传达。选项 3 在内部处理这个问题,但在许多情况下,您自己的代码仍然需要帧感知,这样您就不会消耗太多并阅读下一条消息(除非这是 UDP,每个数据包只有一帧,但是...不是,因为 MemoryMappedFile
)。您还需要考虑如果有效载荷大于预期缓冲区大小会发生什么情况。
"framing"最简单的方法是在每个逻辑块的开头保留 4 个字节;从偏移量 4 开始序列化,然后在偏移量 0 到 3 处写回写回的字节数,在您选择的字节顺序中使用固定的 4 字节布局(通常为 "little")。读取时:读取前 4 个字节以获得长度,然后从段中消耗那么多字节。这基本上就是 *WithLengthPrefix
内部所做的,除了支持一些不同的布局选项。