如何对C / C ++结构进行增量编码以便通过套接字进行传输

时间:2009-10-24 17:38:26

标签: c++ c serialization encoding struct

我需要以相当高的更新速率通过线路(使用UDP套接字,并且可能在某些时候使用XDR)发送C结构,这可能会导致大量冗余和不必要的流量达到几khz。

这是因为,结构中的某些数据有时可能没有变化,所以我认为对当前的C结构进行delta编码与先前的C结构相比似乎是一个好主意,非常像“diff” ”

但我想知道,做这样的事情的最佳方法是什么,理想情况是以可移植的方式确保数据完整性得以维持?是否可以简单地对数据进行异或,并按此进行?

类似地,重要的是该方法保持足够的可扩展性,以便可以将新字段添加到结构中或在必要时重新排序(填充),这听起来好像它还需要版本信息。

任何想法或指针(是否有现有的图书馆?)都将受到高度赞赏!

由于

编辑:感谢所有提供答案的人,非常感谢细节,我意识到我可能不应该提到UDP,因为这实际上不是主要问题,因为已经有了相应的在UDP之上实现的协议解决了上述困难,所以这个问题实际上是特定于delta编码结构的可行方法,而不是特别使用UDP作为传输机制。

4 个答案:

答案 0 :(得分:4)

UDP并不能保证实际接收到给定的数据包,因此将您传输的任何内容编码为“与上次时间的差异”是有问题的 - 您无法知道您的对应方具有相同的关于“最后一次”的想法。基本上你必须在UDP之上构建一些开销来检查收到了什么数据包(用唯一的ID标记每个数据包) - 每个试图走这条路线的人会同意你经常发现自己更多或者在UDP之上重复使用TCP流媒体基础设施......只是,很可能不是那么稳固且发展良好(尽管有时你可以利用有效载荷的非常特殊的特性来获得比普通商品更好的优势老TCP)。

您的传输是否需要单向,发送方到接收方?如果是这种情况(即,接收者发送确认或转发是不可接受的)那么你可以沿着这些线路做的事情真的不多。我想到的一件事是:如果接收器暂时不同步,那么发送者可以发送两种数据包 - 一种包含结构当前值的完整图像,以及一种识别唯一标签,至少每隔5分钟发送一次(实际上,如果接收器错过了其中的两个“大包”,接收器可能会长达15分钟不同步);一个只有最后一个“大数据包”的更新(差异),包括大数据包识别唯一标记和(例如)你提到的XOR的游程编码版本。

当然,一旦准备好了游程编码版本,服务器就会将其大小与整个结构的大小进行比较,如果节省的话,只发送delta类的数据包,否则它也可以比预期更早发送大数据包(可靠性增益)。收到的将跟踪它收到的最后一个大数据包唯一标记,并仅应用与其相关的增量(帮助防止丢失的数据包和无序传递的数据包,具体取决于您希望创建客户端的复杂程度)。

对版本控制的需求& c,取决于你究竟是什么意思(对于结构的C布局应该看起来如何定期沟通的发送者和接收者会有不同的想法吗?他们如何握手知道哪些版本都知道?等等,会增加更多的并发症,但这确实是另一个问题,你在标题中总结的核心问题已经足够大了; - )。

如果你能够偶尔从接收者那里得到元消息(回复或请求重发),那么根据游戏中的各种数字参数,你可以设计不同的策略。我怀疑acks必须非常频繁才能做得很好,所以重新发送一个大数据包(要么是一个特别识别的数据包或“你拥有最新鲜的数据”)的请求可能是剔除选项空间的最佳元策略(否则可能会爆炸;-)。如果是这样,那么发送者可能会对接收者用于请求大包重发的任何策略一无所知,并且您可以在接收者方面尝试各种此类策略,而无需重新部署发送者。

如果没有一些细节,很难提供更多的帮助,即至少所有数字参数的球场数量 - 数据包大小,发送频率,发送器与接收器不同步的容忍程度,一堆网络参数等等。但我希望这种有点通用的分析和建议仍有帮助。

答案 1 :(得分:3)

进行delta编码:

1)定期发送“关键帧”(例如每秒一次)。关键帧是完整的副本(而不是增量),因此如果您因任何原因丢失了通信,则只能在再次“获取信号”之前丢失少量数据。使用一个简单的数据包标头,它允许您检测数据包的开始并知道它包含的数据类型。

2)计算前一个数据包的增量并以紧凑的形式对其进行编码。通过检查您发送的数据类型及其通常的变化方式,您应该能够设计出非常紧凑的增量。但是,您可能需要检查增量的大小 - 在某些情况下,它可能不是有效的编码 - 如果它大于关键帧,您可以只发送另一个关键帧。此时您还可以决定您的增量是有损还是无损。

3)对数据包添加CRC校验(搜索CRC32)。这将允许接收方验证数据包是否已完整接收,允许它们跳过无效数据包。

注意:

  • 小心通过UDP执行此操作 - 它无法保证您的数据包将按照您发送的顺序到达。显然,只有在数据包有序的情况下,delta才会起作用。在这种情况下,您需要为每个数据包添加某种形式的序列ID(第一个数据包为“1”,第二个数据包为“2”等),以便您可以检测到无序接收。您甚至可能需要在接收器中保留“n”个数据包的缓冲区,以便在解码它们时以正确的顺序重新组装它们(当然,这可能会引入一些延迟)。您可能还会错过UDP上的一些数据包,在这种情况下,您需要等到下一个关键帧才能“重新获取信号” - 因此关键帧必须足够频繁以避免灾难性中断在你的通讯中。

  • 考虑使用压缩(例如zip等)。你可能会发现一个完整的数据包可以以拉链友好的方式构建(例如,将数据组合起来,将可能具有相似值(尤其是零)的字节组合在一起)然后进行压缩以使其小于未压缩的增量,并且你根本不需要全力以赴(而且你不必担心数据包订购等)。

修改   - 始终在数据包中使用版本号(或数据包类型),以便将来添加新字段或更改增量编码!无论如何,您都需要这样来区分键/ delta帧。

答案 2 :(得分:1)

我不相信UDP上的delta编码值 - 这本身就是不可靠和乱序的 - 将变得特别容易。相反,我会发送已更改字段的ID及其当前值。如果要向要发送的数据结构添加额外字段,也不需要更改任何内容。如果您想要一种标准的方法,请查看SNMP;这可能是你可以插入的东西,或者它可能有点松懈(它全局限定字段名称并使用ASN.1 - 两者都提供最大的互操作性,但是以数据包中的某些字节为代价)。

答案 3 :(得分:0)

使用类似corba或协议缓冲区的RPC

将DTLS与压缩选项一起使用

使用打包格式

重新利用现有的标头压缩库