当前位置:  开发笔记 > 编程语言 > 正文

在C中解析二进制数据?

如何解决《在C中解析二进制数据?》经验,为你挑选了5个好方法。

有没有关于如何在C中读取和解析二进制数据的库或指南?

我在看一些功能将在网络套接字接收TCP数据包,然后根据规范解析二进制数据,由转码中的信息成为一个更可用的形式.

是否有任何图书馆可以做到这一点,甚至是执行此类事情的入门书?



1> Casey Barker..:

我不得不同意这里的许多回应.我强烈建议你避免将结构转换为传入数据的诱惑.它似乎很有吸引力,甚至可能适用于您当前的目标,但如果代码被移植到另一个目标/环境/编译器,您将遇到麻烦.原因如下:

Endianness:你现在使用的架构可能是big-endian,但你的下一个目标可能是little-endian.或相反亦然.您可以使用宏(例如ntoh和hton)来克服这个问题,但这是额外的工作,并确保每次引用该字段时调用这些宏.

对齐:您正在使用的架构可能能够在奇数寻址偏移处加载多字节字,但许多架构不能.如果一个4字节的字跨越一个4字节的对齐边界,那么负载可能会产生垃圾.即使协议本身没有未对齐的字,有时字节流本身也是未对齐的.(例如,虽然IP标头定义将所有4字节字放在4字节边界上,但以太网标头通常会将IP标头本身推送到2字节边界.)

填充:您的编译器可能会选择紧密打包您的结构而不填充,或者它可能会插入填充以处理目标的对齐约束.我在同一个编译器的两个版本之间看到了这种变化.您可以使用#pragmas强制解决问题,但#pragmas当然是特定于编译器的.

位排序:C位域内的位排序是特定于编译器的.另外,对于运行时代码来说,这些位很难"得到".每次在结构中引用位域时,编译器都必须使用一组掩码/移位操作.当然,你将不得不在某些时候进行掩蔽/移动,但如果速度是一个问题,最好不要在每次参考时都这样做.(如果空间是最重要的问题,那么请使用位域,但要小心.)

这一切并不是说"不要使用结构".我最喜欢的方法是声明所有相关协议数据的友好的native-endian结构,没有任何位域并且不关心问题,然后编写一组使用struct作为中间人的对称打包/解析例程.

typedef struct _MyProtocolData
{
    Bool myBitA;  // Using a "Bool" type wastes a lot of space, but it's fast.
    Bool myBitB;
    Word32 myWord;  // You have a list of base types like Word32, right?
} MyProtocolData;

Void myProtocolParse(const Byte *pProtocol, MyProtocolData *pData)
{
    // Somewhere, your code has to pick out the bits.  Best to just do it one place.
    pData->myBitA = *(pProtocol + MY_BITS_OFFSET) & MY_BIT_A_MASK >> MY_BIT_A_SHIFT;
    pData->myBitB = *(pProtocol + MY_BITS_OFFSET) & MY_BIT_B_MASK >> MY_BIT_B_SHIFT;

    // Endianness and Alignment issues go away when you fetch byte-at-a-time.
    // Here, I'm assuming the protocol is big-endian.
    // You could also write a library of "word fetchers" for different sizes and endiannesses.
    pData->myWord  = *(pProtocol + MY_WORD_OFFSET + 0) << 24;
    pData->myWord += *(pProtocol + MY_WORD_OFFSET + 1) << 16;
    pData->myWord += *(pProtocol + MY_WORD_OFFSET + 2) << 8;
    pData->myWord += *(pProtocol + MY_WORD_OFFSET + 3);

    // You could return something useful, like the end of the protocol or an error code.
}

Void myProtocolPack(const MyProtocolData *pData, Byte *pProtocol)
{
    // Exercise for the reader!  :)
}

现在,您的其余代码只是在友好,快速的struct对象中操作数据,并且只在必须与字节流接口时才调用pack/parse.不需要ntoh或hton,也没有位域来减慢代码速度.


最近又出现了这个问题,重新阅读,我认为Groo错过了这个实现的重点.所以我想更具体地说:这个代码适用于任何宽度或字节序的处理器,只要PROTOCOL是big-endian.协议在定义后通常不会改变,因此它实际上不是问题.

2> kervin..:

在C/C++中执行此操作的标准方法实际上是以'gwaredd'建议的方式转换为结构体

它并不像人们想象的那样不安全.您首先转换为您期望的结构,如在他/她的示例中,然后您测试该结构的有效性.您必须测试最大/最小值,终止序列等.

你在什么平台上必须阅读Unix网络编程,第1卷:套接字网络API.买它,借它,偷它(受害者会理解,这就像偷食物或东西......),但要读它.

在阅读史蒂文斯之后,大部分内容都会更有意义.



3> bortzmeyer..:

让我重申你的问题,看看我是否理解得当.您正在寻找将对数据包进行正式描述的软件,然后生成一个"解码器"来解析这些数据包?

如果是这样,该字段中的引用是PADS.介绍它的一篇好文章是PADS:用于处理Ad Hoc数据的领域专用语言.PADS非常完整,但遗憾的是非自由许可.

有可能的替代方案(我没有提到非C解决方案).显然,没有一个可以被视为完全生产就绪:

binpac

PacketTypes

DataScript

如果你读法语,我在Générationdedécodeursdeformats binaires中总结了这些问题.



4> unwind..:

根据我的经验,最好的方法是首先编写一组基元,从二进制缓冲区读取/写入某种类型的单个值.这为您提供了高可见性,以及处理任何字节序问题的非常简单的方法:只需使函数正确执行即可.

然后,您可以为struct每个协议消息定义s,并为每个消息写入pack/unpack(有些人称之为序列化/反序列化)函数.

作为基本情况,提取单个8位整数的原语可能如下所示(假设char主机上有8位,您可以添加一层自定义类型以确保它也是如此):

const void * read_uint8(const void *buffer, unsigned char *value)
{
  const unsigned char *vptr = buffer;
  *value = *buffer++;
  return buffer;
}

在这里,我选择通过引用返回值,并返回更新的指针.这是一个品味问题,您当然可以返回值并通过引用更新指针.读取函数更新指针,使这些链接成为设计的关键部分.

现在,我们可以编写一个类似的函数来读取16位无符号数量:

const void * read_uint16(const void *buffer, unsigned short *value)
{
  unsigned char lo, hi;

  buffer = read_uint8(buffer, &hi);
  buffer = read_uint8(buffer, &lo);
  *value = (hi << 8) | lo;
  return buffer;
}

这里我假设传入数据是big-endian,这在网络协议中很常见(主要是出于历史原因).你当然可以聪明地做一些指针算法并且不需要临时,但我发现这种方式使它更清晰,更容易理解.在调试时,在这种原语中具有最大透明度是一件好事.

下一步是开始定义特定于协议的消息,并编写读/写原语以进行匹配.在这个级别,考虑代码生成; 如果您的协议以一般的机器可读格式描述,您可以从中生成读/写功能,这可以节省很多麻烦.如果协议格式足够聪明,这会更难,但通常是可行的并且强烈建议.



5> Jon Skeet..:

您可能对Google Protocol Buffers感兴趣,它基本上是一个序列化框架.它主要用于C++/Java/Python(这些是Google支持的语言),但一直在努力将其移植到其他语言,包括C语言.(我根本没有使用过C端口,但我负责其中一个C#端口.)

推荐阅读
mylvfamily
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有