多条数据包的拆包与组包
本内容多条数据的黏包与分包从三个方面说明
**1.串口收发数据 2.TCP收发数据 3.webSocekt收发数据**
一般数据协议都是带校验或与固定的格式,像串口数据一般都带有CRC校验,TCP的其中一种与串口一样,webSocekt的数据格式是json格式,其他的格式下面的方法不一定合适仅供参考
1. 串口接收数据的处理
(1.)串口一般数据格式
(2)串口接收数据一般分为两种:字节接收 空闲包接收。
1)字节接收处理:不用考虑黏包因为就是一个一个字节处理的
处理方式:一般是把接收的数据放到数组里,用两个角标变量记录数据存储的长度与数据使用的位置(也就队列的入队与出队),每次通过这两个数组角标的变量进行组包,组中得到一包的数据。
串口数据放数据
从数组取数据(串口的数据放进数组也一样,只是换一个变量)如图:
2) 空闲接收处理:
空闲接收是接收一帧及一包的数据一般接收存储是用二维数组或者队列
串口数据的接收:
数组取数据
----------------------------------------------------------------
2. TCP数据黏包处理
TCP接收数据得到完整的包比较麻烦,TCP数据包的传输虽然可以保证数据不丢失,但会出现黏包或分包接收的情况。这也就是现在为什么好多物联网公司开发产品,解析接收数据时不选择TCP协议,而改用mqqt协议的原因。
想要把接收的多包数据解析成一个个完整包的tcp数据并不难,因为TCP本身可以保证数据不丢失,无非只需要保证:
1:发送的每包数据都有固定格式,这是解析的前提,我用的是上面的moudle协议格式
2:在接收一包数据不是整包时(只有前半包),先把数据存起来与后面接受的数据包拼接在解析而已。
3:在接收一包数据包含多条数据(或最后一包只有半包)时,只需要先处理整包的,后面如果有半包的就像问题1先缓存在处理即可。
TCP防止数据异常处理只需要保证逻辑处理没有问题即可,我在项目中解析接受一包TCP包的数据解析是:
1:如果接受的是一整包直接处理,并清除缓存。 2:如果接受的是多条整包一条条直接处理,并清除缓存。
2:如果不是整包直接存储缓存,分半包,多条半包。
半包:缓存中解析是不是整包,是直接处理,并清除缓存。
多条半包:多整包+半包 ,半包+多整包,半包+多整包+半包
处理方式都一样:在缓存中只要是多包肯定是多整包或多整包+半包,整包的就直接处理掉即可,真的是屡试不爽!!!
部分代码如下:
1.接受TCP数据
2.数据包解析
/*
* tcp:多客户端的数据拼接--不能有全局变量
* 既然存在分包--又是多个TCP所以就所需要的存储变量-也就需要一个全局的结构体数组 ---不然存在数据混乱的风险
* 分析数据包通过数据长度进行分析 按照第一个包的数据长度进行匹配 ----如果
* 1.刚好一包crc与长度符合,则入队列
* 2.数据包比dataLen短 说明数据被分包了-----记录读取值
* 3.开始就是脏数据---a数据先前半部分 pin包-后半部分
* 解析数据包--数据分为
* uint8_t *Buffer: 接受的新数据包
* int *readLen: 新数据包长度
* int *joinAllLen: 脏数据拼接包的长度
* int *renmaDataLen:新数据来时判断拼接的长度 应该读取的剩余长度 0:表示此包不需要拼接包 x:表示需要拼接bag还需要 此bag需要拼接的长度
* int clientNum:数据拼接成一个整包就入队列
* 返回: -1:拼接失败不向下发送 0:这个包是正确的 1:脏数据拼接成功 0:整包 多整包 -1:不到一个包 1:多个半bag
*/
int dataBagAnaJoinForDataLen(uint8_t *Buffer,int readLen,uint8_t *dirtyBuf,int *joinAllLen,int clientNum){
//1.流程 来一包数据 先看标志位---之前的数据有没有处理完。
//2.没处理完:按照上包拼接的剩余长度 如果crc不对 --重新判断此此包
// 对了:继续判断下包的数据
//3.处理完:
int ret = 0;
if(*joinAllLen == 0){//正常从头开始处理
return allDataFreshJoin(&Buffer[0],readLen,&dirtyBuf[0],joinAllLen,clientNum,0); //0整包 1有多个半 -1:不到一个包
}else if(*joinAllLen > 0){//之前的包有脏数据
if(judgeCRC_rtu(&Buffer[0],Buffer[MODEBUS_DATA_LEN_BIT] + MODEBUS_MIN_LEN) == 0){ //joinAllLen>0 说明上包有未处理完的脏包 但不够一包
return allDataFreshJoin(&Buffer[0],readLen,&dirtyBuf[0],joinAllLen,clientNum,0);//如果crc正确--之前的脏数据全部清除
}else{//之前的bag拼接异常--重新拼接
for(int i=0;i<readLen;i++){
dirtyBuf[i+ *joinAllLen] = Buffer[i];
}
printf_debug("上个包异常重新拼接 \n");
ret = allDataFreshJoin(&dirtyBuf[0],*joinAllLen+readLen,&dirtyBuf[0],joinAllLen,clientNum,1);
if(ret == -1){//连续两个bag都拼接不成功
*joinAllLen = 0;
}
return ret;
}
}else{//拼接异常
*joinAllLen = 0;
printf_debug("接受解析脏数据拼接异常 bug \n");
}
return -1;
}
----------------------------------------------------------------
4. webSocekt数据包接收的处理
有时间继续记录