使用struct在C中进行数据包解析

时间:2017-04-30 04:23:48

标签: c parsing packet

我有一个来自线路的数据包,我有一个表示数据包的结构:

void decodePacket(char *packet) {
 Connect *connect = NULL;
 connect = (Connect *) packet;
 // Here I access the different fields of the packet using the connect struct
 printf("Protocol Name is %s\n", connect->protocolName);
}

我有以下用于解析数据包的C代码:

var module=ons.bootstrap('myApp',['onsen']);
module.controller('MainController',function($scope){
console.log("Main controller");
});


<body  ng-app="FirstProject" ng-controller="MainController">
<ons-tabbar>
  <ons-tab label="tab1" page="tab1.html" active>
  </ons-tab>
  <ons-tab label="tab2" page="tab2.html">
  </ons-tab>
</ons-tabbar>

<ons-template id="tab1.html">
  <ons-page>
    <p style="text-align: center;">
      This is the first page.
    </p>
  </ons-page>
</ons-template>


<ons-template id="tab2.html">
  <ons-page>
    <p style="text-align: center;">
      This is the second page.
    </p>
  </ons-page>
</ons-template>


</body>

在上面的协议中,当字符串从线路进入时,字符串不是空的,因此当我打印协议名称时,我会得到一些奇怪的字符。有没有办法在不改变协议的情况下解决这个问题?

如果协议名称是> 20,char缓冲区将溢出。有没有办法解决这个问题?我是否需要放弃这种解析数据包的方法,只需使用索引并手动解析数据包的每个字节?

由于

3 个答案:

答案 0 :(得分:2)

将数据包转换为结构体非常危险,因为您很少或根本无法控制结构在内存中的实际对齐方式。

你需要阅读C结构包装的失落艺术:http://www.catb.org/esr/structure-packing/

答案 1 :(得分:0)

有几种方法;哪个最好取决于很多因素。

第一种方式。如果您一次读取一个字节,使用指针或索引在接收缓冲区中写入,那么在读取protocolNameLength之后,您就知道protocolName会有多少个字符。那么你可以1)在你的结构中丢弃名字中的尾随字符; 2)在protocolName的末尾添加一个NULL,这样就可以轻松管理名称; 3)在读取protocolName后将写指针移动到正确的位置。您还可以通过将其声明为“char protocolName [many]”来为protocolName保留尽可能多的字符:它不会有太大的成本。

第二种方式。您可以在一个缓冲区中,在缓冲区中接收数据包,然后在两个不同的操作中将接收到的缓冲区memcpy()到您的结构中。第一个memcpy复制前4个成员;第二个将数据包的其余部分复制到正确的目的地。必须对第二个memcpy()执行一些指针运算。第一个memcpy()可以防止名称过长。同样,您可以在使用第一个memcpy复制的数据末尾添加NULL,因此您拥有protocolName的真实C字符串。

第三种方式。您可以使用struct本身作为接收缓冲区,然后将protocolmvel的“real(received)”位置的memmove()用作“正确(设计)”地址。假设您为protocolName []保留了足够的空间,此memmove将始终向后移动数据。同样,如果protocolName(或者在示例中的最后一个元素19(20-1)中),您可以将NULL放在正确的位置。

鉴于protocolNameLength是用16位声明的,我可以假设名字可能很长,而且很奇怪。但如果真的这个名字可能如此冗长,那么移动内存就会浪费CPU。

如果你需要管理很多字段,那么使用memcpy()/ memmove()会有意义,你必须多次这样做。如果您没有大块移动,它是可行的,并且您知道编译器如何打包/填充结构(如果您想在有线协议上叠加C结构,则无论如何都需要此最后一个要求)。

我个人更喜欢第一种方式,如果可能的话;否则,如果字段不是太多,请“手动”解析数据包。

答案 2 :(得分:0)

  

在上面的协议中,字符串在进入时不会以空值终止...当我打印协议名称时,我会得到一些奇怪的字符。有没有办法在不改变协议的情况下解决这个问题?

%s

的精度限制打印宽度
  

如果指定了精度,则不超过那么多字节   写了C11dr§7.21.6.18

// printf("Protocol Name is %s\n", connect->protocolName);
printf("Protocol Name is %.*s\n", 
    (int)(sizeof connect->protocolName), 
    connect->protocolName);

“字符串不是空终止”是一个矛盾。在C中,标准库中使用的字符串始终为空字符终止,否则它不是字符串,而只是字符数组。幸运的是%.*s处理字符数组和字符串,以给定的精度或空字符停止。

以下代码是问题@M.M char *,包括Connect *的对齐方式。这可以通过更高的未发布代码或如下解决。使用char*解码数据包不是最好的解决方案。

void decodePacket(char *packet) {
   Connect *connect = NULL;
   connect = (Connect *) packet;  // bad

// alternative
void decodePacket(char *packet) {
   Connect connect;
   memcpy(&connect, packet, sizeof connect);
   printf("Protocol Name is %.*s\n", 
       (int)(sizeof connect.protocolName), 
       connect.protocolName);
  

如果协议名称是> 20,char缓冲区将溢出。

目前尚不清楚。如果发件人中发生溢出,则接收代码无法解决此问题。可以如上所述修复OP的printf()代码。 OP没有其他代码溢出任何缓冲区。