低带宽连接的二进制编码?

时间:2009-11-30 16:21:58

标签: c# encoding binary compression

在我的应用程序中,我有一个包含结构化数据的简单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#编写的,但任何教程都可以。

8 个答案:

答案 0 :(得分:2)

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

另外,请考虑使用7zip而不是gzip。

答案 1 :(得分:1)

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

They've been ported to C#/.NET并且似​​乎在我的(迄今为止,有点有限)经历中工作得很好。该链接上有一个包与VS集成,并自动从.proto文件创建C#类,非常好。

答案 2 :(得分:1)

将明文格式转换为二进制格式有效的任何内容都可能使压缩率更差,是的。

然而,无论如何,XML优化的二进制格式可能更好。查看Wikipedia page上列出的各种XML二进制格式。我对WBXML有一些经验,但就是这样。

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

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

答案 3 :(得分:1)

我要转储(无论如何,你可以在发送者处解构,并在接收器上重建,在Java中你可以使用自定义的Input / OutputStream来整齐地完成工作)XML。使用固定字段转换为二进制文件 - 数据类型,长度,数据。

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

然后对于每种数据类型,编码方式不同。

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

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

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

答案 4 :(得分:1)

首先要尝试的是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

答案 5 :(得分:1)

我会考虑配置您的应用以响应较小的XML片段;特别是那些足够小以适合单个网络数据包的。

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

答案 6 :(得分:1)

迟到的反应 - 至少在年底前到来; - )

你提到了Fast Infoset。你试过吗?它应该在紧凑性和性能方面给你最好的结果。添加GZIP压缩,最终大小将非常小,您将避免压缩XML的处理代价。 WCF-Xtensions也提供Fast Infoset消息编码和GZIP / DEFLATE / LZMA / PPM压缩(适用于.NET / CF / SL / Azure)。

答案 7 :(得分:0)

这是你所处的腌菜:你正在用Gzip压缩。 Gzip在纯文本上是可怕的,直到你达到狄更斯的总连续作品的长度或大约1200行代码。字典的开销和Gzip用于压缩的其他东西。

对于7500个字符的任务,1Kbps是好的(在最佳条件下需要大约一分钟,但对于<300个字符,你应该没事!)但是如果你真的那么担心,那么你要去为了简洁起见,想要压缩它。以下是我如何做这种规模的事情:

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

即,T代表TYPE。对于INT表示0x01,对于STRING表示0x02等.LENGTH只是一个int ...所以0xFF = 254个字符长等等。示例数据包看起来像:

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

这表示我有一个INT,长度为1,值为0x3F,一个INT,长度为1,值为0x2D,然后是一个STRING,长度为6的空终止“HELLO”(假设为Ascii)。学习System.Text.Encoding.Utf8.getBytes和BitConverter以及ByteConverter的奇迹。

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