我一直在尝试实现一些协议解码器,但每次遇到“简单”问题时我都觉得我解决问题的方式不是最优的,必须有更好的方法来做事情。我正在使用C.目前我正在使用一些固定数据并将其作为文件读取,但稍后它将通过TCP或UDP。
这是问题所在。我正在玩二进制协议。所有字段都是8位长。第一个字段(8位)是数据包类型。所以我在前8位读取并使用开关/情况我调用一个函数来读取数据包的其余部分,因为我知道它的大小/结构。但是......其中一些数据包里面有嵌套数据包,所以当我遇到那个特定的数据包时,我必须再读取另外一个8-16字节的另一个交换机/情况,看看下一个数据包的类型是什么,然后打开。 (幸运的是,数据包只嵌套2或3深)。 只有将整个数据包解码后,才能将其处理到我的状态机进行处理。
我想这也是一个更普遍的问题。你一次从套接字读取多少数据?越多越好?与协议标题中的“相似”一样多吗?
所以即使这个协议是相当基本的,我的代码是一大堆switch / case语句,我从文件/套接字中做了很多阅读,我觉得这不是最优的。我的主要目标是尽可能快地制作这个解码器。对于那些经验丰富的人来说,这是要走的路还是有一种更好的方式,我还没想到呢?这个问题有什么优雅的解决方案吗?
答案 0 :(得分:11)
我推荐这种方法:
伪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
}
通常,为了获得更高的性能,最好尽可能多地阅读。
答案 1 :(得分:3)
抵制过早优化的诱惑。首先让它工作,然后才能考虑是否需要优化。如果你这样做,那就科学地做一下:先对你的代码进行基准测试,然后首先选择最低成本的果实,不要依赖于直觉。
不要忘记,无论您是从文件还是套接字读取,您的操作系统都可能会缓冲数据本身。尽管如此,重复的系统调用很可能成为瓶颈,因此它们可能是一个简单的优化获胜。在以前的工作场所,我们通过让数据包标头显式编码其长度(从不超过8k)来避免这个问题:这样我们就可以确切知道批量读入数组的数量,然后我们自己的缓冲代码接管了。
答案 2 :(得分:2)
请注意read()
完全免费暂时忽略您提供的尺寸并尝试在拱形对齐边界(8/16/32/64)中读取。 read()
可以自由地执行此操作,只要它返回您所请求的确切数字(或更少)字节即可。因此,可能已经在幕后进行了相当多的优化。
如果您意识到这一点,那么您立即倾向于构造事物,以便您阅读可能的最大块,这通常意味着调用read()
(单独或通过其他使用它的函数)处理数据包的次数最少。
越早在自己的地址空间中获取内存中的数据越好 - 理想情况下最少可能(直接或间接)调用read()
。提示 - 如果从文件描述符或流中获得输入,则使用read()
。
答案 3 :(得分:1)
您一次从套接字读取多少数据?
是TCP还是UDP:它是面向流还是面向数据包?在知道消息类型之前,您是否有办法知道消息的长度?如果没有,你可以(例如通过更改协议以确保第一个字段包含/定义消息的长度吗?
要做你正在做的事情,你需要从套接字中多次阅读。如果您可以将整个消息读入您自己的内存/ RAM,然后在那里进行解码,则可能更快/更容易。
答案 4 :(得分:1)
PADS旨在帮助您解决此类问题。它将为您生成一个非常有效的C解析器。您编写了数据包格式的声明性描述,PADS从那里获取它。