Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# protobuf网络消息序列化大小属性_C#_Protobuf Net - Fatal编程技术网

C# protobuf网络消息序列化大小属性

C# protobuf网络消息序列化大小属性,c#,protobuf-net,C#,Protobuf Net,我们使用protobuf net在一个应用程序中对消息进行序列化和反序列化,该应用程序的公共协议基于Google协议缓冲区。该库非常优秀,涵盖了除此之外的所有需求:在消息实际序列化之前,我们需要找出以字节为单位的序列化消息长度 一年半前已经有人问过了,根据Marc的说法,唯一的方法是序列化到MemoryStream,然后读取.Length属性。在我们的例子中,这是不可接受的,因为MemoryStream在幕后分配字节缓冲区,我们必须避免这种情况 同一回复中的这句话给了我们希望,毕竟这可能是可能的

我们使用protobuf net在一个应用程序中对消息进行序列化和反序列化,该应用程序的公共协议基于Google协议缓冲区。该库非常优秀,涵盖了除此之外的所有需求:在消息实际序列化之前,我们需要找出以字节为单位的序列化消息长度

一年半前已经有人问过了,根据Marc的说法,唯一的方法是序列化到MemoryStream,然后读取
.Length
属性。在我们的例子中,这是不可接受的,因为MemoryStream在幕后分配字节缓冲区,我们必须避免这种情况

同一回复中的这句话给了我们希望,毕竟这可能是可能的:

如果您澄清用例是什么,我相信我们可以很容易地做到 可用(如果尚未提供)

这是我们的用例。我们有大小在几个字节到两兆字节之间变化的消息。应用程序预先分配用于套接字操作和序列化/反序列化的字节缓冲区,一旦预热阶段结束,就不能创建额外的缓冲区(提示:避免GC和堆碎片)。字节缓冲区基本上是池化的。我们还希望尽可能避免在缓冲区/流之间复制字节

我们提出了两种可能的策略,这两种策略都需要预先确定消息大小:

  • 使用(大)固定大小的字节缓冲区,并序列化所有可以放入一个缓冲区的消息;使用
    套接字发送缓冲区的内容。发送
    。我们必须知道下一条消息何时无法放入缓冲区并停止序列化。如果没有消息大小,实现这一点的唯一方法是等待在
    序列化期间发生异常
  • 使用(小)可变大小字节缓冲区,并将每条消息序列化为一个缓冲区;使用
    套接字发送缓冲区的内容。发送
    。为了从池中检出大小合适的字节缓冲区,我们需要知道序列化消息有多少字节
  • 由于协议已定义(我们无法更改此设置),并且要求消息长度前缀为Varint32,因此我们无法使用
    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(这将跳过字段号)进行编码。感谢您的澄清,我想我错过了那个。谢谢你的详细回复。我们仍在评估可能的方法,并试图找到尽可能少的数据处理和缓冲区分配的最佳方法。