C# protobuf网络消息序列化大小属性
我们使用protobuf net在一个应用程序中对消息进行序列化和反序列化,该应用程序的公共协议基于Google协议缓冲区。该库非常优秀,涵盖了除此之外的所有需求:在消息实际序列化之前,我们需要找出以字节为单位的序列化消息长度 一年半前已经有人问过了,根据Marc的说法,唯一的方法是序列化到MemoryStream,然后读取C# protobuf网络消息序列化大小属性,c#,protobuf-net,C#,Protobuf Net,我们使用protobuf net在一个应用程序中对消息进行序列化和反序列化,该应用程序的公共协议基于Google协议缓冲区。该库非常优秀,涵盖了除此之外的所有需求:在消息实际序列化之前,我们需要找出以字节为单位的序列化消息长度 一年半前已经有人问过了,根据Marc的说法,唯一的方法是序列化到MemoryStream,然后读取.Length属性。在我们的例子中,这是不可接受的,因为MemoryStream在幕后分配字节缓冲区,我们必须避免这种情况 同一回复中的这句话给了我们希望,毕竟这可能是可能的
.Length
属性。在我们的例子中,这是不可接受的,因为MemoryStream在幕后分配字节缓冲区,我们必须避免这种情况
同一回复中的这句话给了我们希望,毕竟这可能是可能的:
如果您澄清用例是什么,我相信我们可以很容易地做到
可用(如果尚未提供)
这是我们的用例。我们有大小在几个字节到两兆字节之间变化的消息。应用程序预先分配用于套接字操作和序列化/反序列化的字节缓冲区,一旦预热阶段结束,就不能创建额外的缓冲区(提示:避免GC和堆碎片)。字节缓冲区基本上是池化的。我们还希望尽可能避免在缓冲区/流之间复制字节
我们提出了两种可能的策略,这两种策略都需要预先确定消息大小:
套接字发送缓冲区的内容。发送。我们必须知道下一条消息何时无法放入缓冲区并停止序列化。如果没有消息大小,实现这一点的唯一方法是等待在序列化期间发生异常
套接字发送缓冲区的内容。发送。为了从池中检出大小合适的字节缓冲区,我们需要知道序列化消息有多少字节
SerializeWithLengthPrefix
方法
那么,是否有可能添加一个方法来估计消息大小而不将其序列化到流中呢?如果它不适合当前的特性集和库的路线图,但可行,那么我们有兴趣自己扩展库。我们也在寻找替代方法,如果有。如前所述,这不是立即可用的,因为代码有意尝试对数据进行单次传递(尤其是
IEnumerable
等)。不过,根据您的数据,它可能已经进行了适量的复制,以考虑到子消息也带有长度前缀,因此可能需要处理。通过在消息内部使用“分组”子格式,可以大大减少这种杂耍,因为分组只允许转发构造,而不允许回溯
那么,是否有可能添加一个方法来估计消息大小而不将其序列化到流中呢
估计几乎是无用的;因为没有终结者,所以它需要精确。最终,如果不进行实际操作,尺寸有点难以预测。v1中有一些用于大小预测的代码,但目前似乎更倾向于使用单通道代码,并且在大多数情况下,缓冲区开销是正常的(有代码可以重用内部缓冲区,因此不会花费所有时间为小消息分配缓冲区)
如果您的消息在内部仅转发(分组),那么欺骗可能是将其序列化为一个伪流,该流可以测量,但会删除所有数据;但是,最终会连续两次
关于:
并且要求消息长度前缀为Varint32,我们不能使用SerializeWithLengthPrefix
方法
我不太确定我是否看到了这种关系——它允许在这里使用一系列的格式等;也许你能更具体一点
重新复制周围的数据-我在这里玩的一个想法是使用次正规形式作为长度前缀。例如,在大多数情况下,5个字节是足够的,因此它可以保留5个字节,然后简单地覆盖,而不进行压缩(因为八位字节10000000
仍然意味着“零并继续”,即使它是多余的)。这仍然需要缓冲(以允许回填),但不需要对数据进行存储和移动
最后一个简单的想法是:序列化为
文件流
;然后写入文件长度和文件数据。显然,它会为IO交换内存使用。关于长度前缀和SerializeWithLengthPrefix:该方法可以写入编码为Base128、Fixed32和Fixed32BigEndian的前缀,但它不支持Varint32类型。我们的协议定义了以下消息结构:[type:varint32][length:varint32][actual protobuf message]。@Boris这正是带有字段号的base-128。字段编号应为===类型(可能有>>3个移位)。我需要查看确切的字节,但这可能是可用的。如果没有,只需手动添加类型,并使用base-128和字段0(这将跳过字段号)进行编码。感谢您的澄清,我想我错过了那个。谢谢你的详细回复。我们仍在评估可能的方法,并试图找到尽可能少的数据处理和缓冲区分配的最佳方法。