当前位置:C++技术网 > 精选软件 > 网络数据包拆包失败,原因竟然是这个基本的问题

网络数据包拆包失败,原因竟然是这个基本的问题

更新时间:2016-12-01 16:55:18浏览次数:1+次

    网络数据包经常会多个包一起连在一起发送到了目的地。目的地需要将两个包拆开成两个单独的数据包处理,否则忽略了叠包的情况,只处理了第一个包,那么第二个包就丢掉了。这样对方在超时后,还会重新发送一个包过来。如果叠包发生的情况比较多,那么会增加很多的不必要的数据重传。
    一开始做服务器程序开发,经验不足,我并没有处理叠包的情况。后来同事说,叠包是常有的事,必须处理。然后我打印日志看看接受的数据的情况,结果发现了很多叠包情况,甚至有时候,连续叠加了5个数据包。这让我情何以堪!
    所以,就要做好叠包处理。虽然对方会重传,可以确保数据不会丢失,不过会降低效率,引发其他问题。万一对方没有使用重传机制,数据不就丢失了。
    需要注意的是,我们这里说的是应用层的数据包。TCP接收到的数据,传递给应用层,可能不止一个数据包的哦。
    所以我开始写拆包代码,想着其实也挺简单的,然后写了代码如下(示意代码):
#include "stdio.h"
void main()
{
const char arr[10]={0x10,0x41,0xAA,0x55,0x23,0x45,0x14,0x63,0x87,0x01};
    int pos=-1;
    for (int i=0;i<9;i++)
    {
        if (i!=0 && arr[i]==0xAA && arr[i+1]==0x55)
        {
            pos = i;
        }
    }
    printf("pos=%d\n",pos);
}

    咋一看,输出的结果应该是:pos=2。实际上,应该是-1。这是我通过TCP调试助手发了连续两个数据包后,发现服务器只处理了一条。通过调试才发现,拆包在这里的比较失败了。
    一开始硬是没有看出来哪里出错了,然后就将arr[i]先存到一个int临时变量,然后发现变量的值是0xffffffAA和0xffffff55这样的。这个表示这两个值是负数,而0xAA和0x55是正数,自然比较的话,就不同,拆包失败。
    问题在哪呢?因为arr数组是char类型,也就是有符号类型,所以正数的最大值不会超过0x80,而0xAA是大于0x80的。超过了也就存储的是负数了。所以打印arr[2]的值是-86哦。而在实际比较的0xAA的十进制是170.所以两个值不相等。
    我们将arr[2]打印出来看看就知道了:
printf("a=%d,pos=%d\n",arr[2],pos);

    结果如图:

    所以改进的代码就是将arr数组定义为unsigned char类型。一般在协议报文、计算校验码之类的,都是单字节正数值,所以不能使用char类型。如果我们无法改变arr数组的类型,那么就要将数组的值转存为unsigned char类型,或者强制转换为
unsigned char类型,然后再比较。
    开始就以正确的类型存储,或者后面用正确的类型解释(转换类型),都是可以的。不过一个好的习惯就是用正确的类型存储,以免后面淡忘了,就出错了。
    修正后的代码如下:
#include "stdio.h"
void main()
{
const unsigned char arr[10]={0x10,0x41,0xAA,0x55,0x23,0x45,0x14,0x63,0x87,0x01};
    int pos=-1;
    for (int i=0;i<9;i++)
    {
        if (i!=0 && arr[i]==0xAA && arr[i+1]==0x55)
        {
            pos = i;
        }
    }
    printf("pos=%d\n",pos);
}

    写这篇文章的目的,是因为之前在实现CRC校验的时候也是因为这个出现Bug,另外一个同事使用char,和我使用unsigned char得到的值不一样,所以校验失败。这次我载在了同一个错误了。需要好好反思总结下,并扩展到同类的问题,防止今后再犯。也希望这个总结能够给读者你一个提醒。