协议解析在C

问题描述:

我一直在试图执行一些协议解码器玩耍,但每次我碰到一个“简单”的问题,我觉得我解决这个问题的办法是不是最佳的,必须有一个更好的做事的方式。我正在使用C.目前我正在使用一些罐头数据并将其作为文件读取,但稍后将通过TCP或UDP进行读取。协议解析在C

这是问题所在。我目前正在工作中使用二进制协议。所有字段都是8位长。第一个字段(8位)是分组类型。所以我读了前8位数据,并使用一个开关/情况函数来读取数据包的其余部分,因为我知道它的大小/结构。然而,一些这些包都嵌套在他们的包,所以当我遇到特定数据包然后我要读另一个8-16字节有另一台交换机/箱,看看下一个数据包类型和和。 (幸运的是,数据包只能嵌套2或3个深度)。 只有一次我有整个数据包解码我可以处理它交给我的状态机处理。

我想这可能是一个更普遍的问题为好。您需要从套接字一次读取多少数据?越多越好?就像协议头文件中的“相似”一样?

因此,尽管这个协议是非常基本的,我的代码是一大堆的开关/ case语句,而我做的从文件/插座,我觉得不是最优读了很多。我的主要目标是尽可能快地制作这个解码器。对于那些更有经验的人来说,这是要走的路还是有更好的方式,我还没有想出来呢?任何优雅的解决这个问题?

+0

非常感谢大家回复!所有的回复都很有用,给了我很多新的想法,正是我期待的! – NomadAlien 2010-06-07 13:37:09

我推荐这种方法:

  1. 阅读所有你可以从文件/插座(从实际协议分开的数据通信)
  2. 传递您已经阅读到的程序来处理数据的数据

伪C代码(想象destinationBuffer是circular buffer - 在需要分析大量输入数据的应用情况下,我相信这样的数据结构是至关重要的):

forever() 
{ 
    // this function adds data to the buffer updating it 
    read_all_you_can(destinationBuffer); 
    ... 
    handle_data(destinationBuffer); 
    // the buffer is automatically adjusted in order 
    // to reflect how much of the data was processed 
} 

通常最好是尽可能多地阅读以获得更多的性能。

+0

该死!我没有想到一个循环缓冲区!这将解决我读取数据的问题,并将数据包减半! – NomadAlien 2010-06-04 13:08:33

+0

@ nomad.alien这是处理流时必须的。你可以考虑几乎任何东西 - 标准输入,文件,套接字,管道 - 通常是UNIX中的文件描述符。 – INS 2010-06-04 14:28:35

多少数据你有从插座时间阅读?

它是TCP还是UDP:它是面向流的还是面向数据包的?在知道消息类型之前,您是否有办法知道消息的长度?如果没有,你可以(例如,通过改变协议,以确保第一个字段包含/定义消息的长度?)

要做你正在做的事情,你需要从套接字读几次。如果你可以将整个消息读入你自己的内存/ RAM中,然后在那里解码它,那么它会更快/更容易;

+0

不幸的是,我不知道数据包的大小,直到我知道数据包的类型。它只是不“觉得正确”,我必须一次从套接字/文件中读取8位数据以决定如何处理它。但我想我必须在某个地方做。这对我来说是第一次尝试,所以可能是我没有做错任何事情,这是做到这一点的正确方法。谢谢回复。 – NomadAlien 2010-06-04 13:03:58

+0

嗯,如果有一个描述数据包类型和大小的标题会更好,所以解析器将更少的努力来评估数据包内容 – pcent 2010-06-04 13:42:07

抵制过早优化的诱惑首先让它工作,然后才应该考虑是否需要优化。如果你这样做,科学地做:基准你的代码,首先去找最低的水果,不要依赖直觉。

不要忘记你的操作系统可能会缓冲数据本身,w无论你是从文件或套接字读取。尽管如此,反复的系统调用可能是一个瓶颈,所以他们可能是一个简单的优化胜利。在以前的工作场所,我们通过让数据包头明确地对其长度进行编码(从不超过8k)来避免这个问题:通过这种方式,我们确切地知道需要多大量读入数组,然后我们自己的缓冲代码才能接管。

+0

我在想同样的事情,但没有数据包大小可言,因为所有字段是8位字段,因此每个数据包都有一个固定的大小,除了允许嵌入字段的一个或两个数据包,但这些嵌入字段的大小也是固定的,但这对于我来说是“难看”的部分,我必须一次读取8位数据,通过几个switch/case语句来了解我拥有的。 – NomadAlien 2010-06-04 13:06:45

请记住,read()是完全免费的暂时忽略你给它的大小,并尝试读取在拱对齐的边界(8/16/32/64)。 read()可以*地做到这一点,只要它返回您请求的确切数量(或更少)的字节。所以,幕后可能已经有相当多的优化了。

如果意识到这一点,所以你看是可能的,这往往意味着调用read()(自有或通过使用它的一些其他功能)最少的最大块你马上倾向于构建东西有可能处理一个数据包。

您越早在自己的地址空间获取内存中的数据,越好 - 理想情况是尽可能少(直接或间接)调用read()。提示 - 如果您从文件描述符或流中获取输入,则使用read()

PADS旨在帮助您准确解决这类问题。它会为你生成一个非常高效的C语言分析器。你写了一个关于数据包格式的声明性描述,PADS从那里获取它。

+0

谢谢诺曼。我会研究它。 – NomadAlien 2010-06-07 13:37:43