Thrift 如何处理 Zlib 刷新标记被分割成多个消息?

How does Thrift handle Zlib flush markers being split over multiple messages?

我有一个应用程序,它有一个 C++ 服务器和一个使用 Apache Thrift 的 C# 客户端。我在服务器上使用 TZlibTransport.cpp 进行 zlib 压缩,并使用 Ionic.Zlib 的包装器在客户端解压缩数据,大部分时间都有效。

我注意到在非常特殊的情况下,客户端会因以下错误之一而崩溃:

Thrift.Protocol.TProtocolException: Missing version in readMessageBegin, old client?
   at Thrift.Protocol.TBinaryProtocol.ReadMessageBegin()

Ionic.Zlib.ZlibException: Bad state (invalid block type)
   at Ionic.Zlib.InflateManager.Inflate(FlushType flush)

我发现在所有发生这些错误的情况下,服务器都发送了两个包,一个刚刚超过 1024 字节(这是 TZlibTransport.cpp 使用的 compressed write buffer 的大小) , 和 5-8 个字节之一。看第二个包上的数据,发现是zlib用的flush marker,加了两次,

ff ff 00 00 00 ff ff

第一个标记的第一部分位于上一个包的末尾。如果我稍微增加缓冲区的大小,使其有足够的 space 在一个包中写入标记,则不会发生崩溃,所以 我相信是这个标记被添加了两次这就是导致问题的原因。然而,仅更改此缓冲区大小并不是解决方案,因为这意味着错误发生在应用程序的其他地方。

我查看了 zlib,发现如果缓冲区中没有足够的 space (https://github.com/madler/zlib/issues/149),这是预期的行为。然而,我还没有找到任何人遇到过这个导致节俭问题的人。

因此,我的问题是,对于特定的数据长度,thrift 是否应该将标记拆分到多个包中,以及客户端应该如何处理这个问题。

看起来问题不在于标记被发射了两次,而只是第一个标记没有完全适合缓冲区。如果输出只是 ff ff,您将遇到完全相同的问题和相同的错误消息。 ff 无法启动 deflate 流,因为它提供了无效的块类型 (3)。

根据您的描述,Thrift 中似乎存在错误,因为它无法确保and/or检查所有压缩数据是否真正适合缓冲区。