【问题标题】:Binary encoding for low bandwidth connections?低带宽连接的二进制编码?
【发布时间】:2009-12-30 20:35:14
【问题描述】:

在我的应用程序中,我有一个包含结构化数据的简单 XML 格式文件。每个数据条目都有一个数据类型和一个值。类似的东西

<entry>
  <field type="integer">5265</field>
  <field type="float">34.23</field>
  <field type="string">Jorge</field>
</entry>

现在,这种格式允许我们以人类可读的形式获取数据,以便检查各种值,以及轻松执行文件的转换和读取以实现互操作性。

问题是我们的连接带宽非常低(大约 1000 bps,是的,这就是比特每秒),因此 XML 并不是传输数据的最佳格式。我正在寻找将 xml 文件编码为更适合传输的二进制等效文件的方法。

你知道关于这个问题的任何好的教程吗?

此外,我们在发送之前压缩数据(简单的 GZIP),所以如果我使用二进制文件,我有点担心会丢失压缩率。 大小会受到严重影响(压缩时),以至于一开始就尝试优化它是个坏主意吗?

注意:这不是过早的优化,它是必需的。 1000 bps 的带宽非常低,因此每个字节都很重要。

注意2:应用程序是用c#编写的,但任何教程都可以。

【问题讨论】:

    标签: c# encoding binary compression


    【解决方案1】:

    尝试使用ASN.1。打包的编码规则应该自己产生一个相当不错的压缩形式,并且 xml 编码规则应该产生与您现有的 xml 等效的东西。

    另外,考虑使用 7zip 代替 gzip。

    【讨论】:

      【解决方案2】:

      您可能想调查Google Protocol Buffers。它们产生的有效载荷比 XML 小得多,尽管不一定是最小的有效载荷;它们是否适合您的使用取决于很多因素。不过,它们肯定比从头开始设计自己的方案要容易。

      They've been ported to C#/.NET 并且在我(到目前为止,有些有限的)经验中似乎工作得很好。该链接上有一个包可以与 VS 进行某种程度的集成,并从 .proto 文件自动创建 C# 类,这非常好。

      【讨论】:

      • 看起来相当不错,无论是直接使用它们还是提取有用的东西。你知道他们是否支持将 XSD 转换为 .proto 文件吗?
      • 为了清楚起见,protobuf-net 并不是真正的“端口”——它是(由我)重写以适应 .NET 典型的编程风格。有关端口,请参阅 dotnet-protobufs。
      • @Jorge:我不知道这样的工具,但我怀疑您可能能够为这样的转换编写 XSLT。 @Marc:抱歉,我并不是要尽量减少您的工作。谢谢!
      • 我没有那样做 ;-p 我只是想澄清有并行的 C# 实现。
      【解决方案3】:

      我会转储(无论如何传输,您可以在发送方解构,并在接收方重构,在 Java 中,您可以使用自定义 Input/OutputStream 来巧妙地完成工作)XML。使用固定字段的二进制 - 数据类型、长度、数据。

      假设您有 8 个或更少的数据类型,请将其编码为 3 位。然后是长度,例如,作为 8 位值 (0..255)。

      然后对每种数据类型进行不同的编码。

      • 整数/浮点数:BCD - 每位 4 位,使用 15 作为小数点。或者只是原始位本身(可能需要 8 位 int、16 位 int、32 位 int、64 位长、32 位浮点、64 位双精度的不同数据类型)。
      • 字符串 - 你能用 7 位 ASCII 代替 8 位吗?等等。所有大写字母 + 数字和一些标点符号都可以让您降低到每个字符 6 位。

      您可能希望在其前面加上要传输的字段总数。如果传输有损,则执行 CRC 或 8/10 编码,但希望系统已经处理了。

      但是,不要低估 XML 文本的压缩效果。我当然会做一些计算来检查正在实现多少压缩。

      【讨论】:

        【解决方案4】:

        任何能有效地将明文形式转换为二进制的方法都可能使压缩率更差,是的。

        但是,XML 优化的二进制格式很可能会比压缩文本更好。查看Wikipedia page 上列出的各种 XML 二进制格式。我对 WBXML 有一点经验,但仅此而已。

        正如 JeeBee 所说,老实说,自定义二进制格式可能是最有效的方法。您可以尝试到 gzip 它,但结果将首先取决于数据是什么样的。

        是的,正如 Skirwan 所说,Protocol Buffers 在这里是一个相当明显的候选者 - 但您可能需要考虑自定义浮点表示,具体取决于您的实际需求。如果您只需要 4SF(并且您知道比例),那么发送一个两字节整数可能是最好的选择。

        【讨论】:

        • 不用太担心浮点数,因为几乎所有内容都存储为小数
        【解决方案5】:

        首先要尝试的是gzip;除此之外,我会尝试 protobuf-net - 我可以很容易地想到几种编码方法,但这取决于您如何构建 xml,以及您是否介意一些代码在两种格式之间进行填充。特别是,我可以想象将不同的数据类型表示为或者同一类型上的 3 个可选字段,抽象合同的 3 个不同子类。

        [ProtoContract]
        class EntryItem {
            [ProtoMember(1)]
            public int? Int32Value {get;set;}
            [ProtoMember(2)]
            public float? SingleValue {get;set;}
            [ProtoMember(3)]
            public string StringValue {get;set;}
        }
        [ProtoContract]
        class Entry {
            [ProtoMember(1)]
            public List<EntryItem> Items {get; set;}
        }
        

        有测试:

        [TestFixture]
        public class TestEntries {
            [Test]
            public void ShowSize() {
                Entry e = new Entry {
                    Items = new List<EntryItem>{
                        new EntryItem { Int32Value = 5265},
                        new EntryItem { SingleValue = 34.23F },
                        new EntryItem { StringValue = "Jorge" }
                    }
                };
                var ms = new MemoryStream();
                Serializer.Serialize(ms, e);
                Console.WriteLine(ms.Length);
                Console.WriteLine(BitConverter.ToString(ms.ToArray()));
            }
        }
        

        结果(21 字节)

        0A-03-08-91-29-0A-05-15-85-EB-08-42-0A-07-1A-05-4A-6F-72-67-65
        

        【讨论】:

        • 这看起来很有趣,无论是语法还是结果。您不会碰巧知道它与 Fast Infoset 相比如何,对吧?
        • 嗯,但是...再次假设我事先知道我将传输哪些数据结构(因此 EntryItem 是固定的)。其实我只知道一个Entry项可能有字段,每个字段都会有一个类型和一个值(一个对象)。我们通过反映对象并在运行时获取字段类型来生成 XML 文件。 protobuf-net 可以做同样的事情吗?如果现在,我可以将 xml 文件“转换”为与 protobuffer 兼容的东西吗??
        • 我没有直接将它与快速信息集进行比较。完全是动态的……嗯,它也使用反射——所以这取决于:你能用必要的属性装饰你的对象吗?就其性质而言,它包含任何有线格式的元数据,因此您必须提前知道这一点,才能序列化/反序列化一个完整的对象。使用一些额外的代码,您可以确保意外数据的往返安全,并手动查询额外的数据,但这并不像提前知道对象模型那么简单。泛型可能有用,但再次它需要预先知道所有预期的类型。
        【解决方案6】:

        我会考虑配置您的应用以响应较小的 XML 片段;特别是那些小到可以放入单个网络数据包的数据包。

        然后按照对用户的重要性排列您的数据传输顺序,以便他们可以看到有用的东西,甚至可能在所有数据到达之前开始处理它。

        【讨论】:

        • 我们已经这样做了 :) 但这是一个很好的建议……但由于数据完整性,实施起来并不容易。
        【解决方案7】:

        迟到的反应——至少是在年底之前 ;-)

        您提到了快速信息集。你试过了吗?它应该在紧凑性和性能方面为您提供最佳结果。添加 GZIP 压缩,最终大小会非常小,并且您将避免压缩 XML 的处理损失。 WCF-Xtensions 也提供快速信息集消息编码和 GZIP/DEFLATE/LZMA/PPMs 压缩(适用于 .NET/CF/SL/Azure)。

        【讨论】:

          【解决方案8】:

          这就是你所处的泡菜:你正在用 Gzip 压缩东西。 Gzip 在纯文本上很糟糕,直到您达到狄更斯的全部连接作品或大约 1200 行代码的长度。字典和 Gzip 用于压缩的其他东西的开销。

          1Kbps 适合 7500 个字符的任务(在最佳条件下大约需要一分钟,但对于

          T[ype]L[ength][data data data]+
          

          即T代表TYPE。比如 0x01 表示 INT,0x02 表示 STRING 等。LENGTH 只是一个 int ......所以 0xFF = 254 个字符长等。示例数据包如下所示:

          0x01 0x01 0x3F 0x01 0x01 0x2D 0x02 0x06 H E L L O 0x00
          

          这表示我有一个长度为 1、值为 0x3F 的 INT、一个长度为 1、值为 0x2D 的 INT,然后是一个长度为 6 的空终止“HELLO”的字符串(Ascii 假设)。了解 System.Text.Encoding.Utf8.getBytes 和 BitConverter 和 ByteConverter 的神奇之处。

          供参考请参阅This page,了解 1Kbps 到底是多少。真的,对于你正在处理的大小,你应该没问题。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多