昨天问了某位美女姐姐一个逻辑问题。由于楼主嘴太笨,语言表达实在不怎么的。到最后也没有解释清楚,最后放弃口头叙述,特写此博客,供美女姐姐参看。要是再不懂,说明我的文字表达也不怎么的,不知到美女姐姐怎么看。
linux下,tcp并发服务器接收数据时,如果网路阻塞,服务器来不处理接收到的数据,就会
出现网络粘包的现象。那么就需要将特定的数据包(应用层自定义协议)进行分包。
应用层数据包格式:
每一包完整的数据是以7e开头,7e结尾的。
例如:7e....字母数字...7e 就是说头尾7e中间是以字母数字组成的数据段,而且这些数据段中也不可能出现7e。数据包是以hex格式,也就是16进制传输。那么服务器接收到数据之后,也就要以hex格式进行处理。
下面分析下数据包粘包的不同情况:
ps:楼主太笨,想了这些情况不知到死了多少脑细胞,唉!
一个或者两个数据包粘包的情况:
1.以7e开头的数据包
7e......7e
一个完整包 ,正常处理
7e......
一个半包,保存起来等待下半包拼接
7e......7e7e......
一个完整包,一个半包,保存半包,等待下半包拼接
7e......7e..........
一个完整包,一个错包(不知到什么时候会发生)
7e7e........
两个7e开头,需要判断上一次接受是否存储有上半包,如果有将第一个7e拼接,如果没有丢掉第一个7e,将第二个7e的半包进行存储
7e7e......7e
两个7e开头,需要判断上一次接受是否存储有上半包,如果有将第一个7e拼接,如果没有丢掉第一个7e,然后正常处理第二个包
2.不是以7e开头的数据包
..........7e
一个半包的情况,需要与上一次的半包拼接,如果上次没有半包,就丢掉
..........7e7e......
一个下半包,一个上半包,与前一次的上半包拼接成完整包后,保存第二个上半包(如果没有上半包,就丢掉---比较坑!)
..........7e7e......7e
一个下半包和一个整包,与前一次的上半包拼接成完整包,接着正常处理第二个完整包(如果没有上半包,就丢掉---这个更坑!)
虽然楼主只是分析了,上面的最多两个包粘连的情况。可是也可能有很多包粘在一起。楼主在程序处理的时候,用一个接口只处理拼包和整包。另外一个接口进行循环处理,如果第一此解析处理,数据段已经完了就退出。如果没有就继续循环进行处理。
下面附上代码:
stick_bag.h
#ifndef _STICK_BAG_H_
#define _STICK_BAG_H_
#include "main.h"
/*粘包不同形式的变量*/
#define S1 1 /*一个完整的包,而且长度刚好*/
#define S2 2 /*一个完整的包,还有数据*/
#define S3 3 /*7e开头的半包*/
#define S4 4 /*拼成一个完整包,下半包只有一个7e*/
#define S5 5 /*拼一个完整的包,还有数据*/
#define SFAULT -1 /*错误包*/
int Stick_Bag(char *recv_buffer,int len,char *half_bag,int *half_len);
int Get_Bag(char *buffer,int len,char *bag,int *bag_len,char *half_bag,int
*half_len);
#endif
stick_bag.c
#include "./include/stick_bag.h"
#include "./include/print.h"
/*
* 加入接收数据队列
对接收到的数据包进行分包出理,防止出现连包现象
*/
int Stick_Bag(char *recv_buffer,int len,char *half_bag,int *half_len)
{
char bag[1024] = {'\0'};
int bag_len = 0;
int off = 0;
int ret ,i = 0;
while(i < len){
ret = Get_Bag(recv_buffer + off,len - off,bag,&bag_len,half_bag,half_len);
if(ret == S1){
printf("一个完整的包\n");
printf("bag_len:%d\n",bag_len);
print(bag,bag_len);
memset(half_bag,'\0',1024);
*half_len = 0;
return 0;
}else if(ret == S2){
off = off + bag_len;
i = off;
printf("一个完整的包,还有数据\n");
/*加入消息队列*/
print(bag,bag_len);
memset(bag,'\0',bag_len);
memset(half_bag,'\0',1024);
*half_len = 0;
}else if(ret == S3){/*半包保存起来*/
printf("半包保存起来\n");
return 0;
}else if(ret == S4){
off = off + 1;
i = off;
/*加入数据队列*/
printf("拼成一个包,下半包只剩一个0x7e:\n");
print(bag,bag_len);
memset(bag,'\0',bag_len);
memset(half_bag,'\0',1024);
*half_len = 0;
}else if(ret == S5){
off = off + bag_len - (*half_len);/*拼完包,还有数据,偏移量*/
i = off;
/*加入数据队列*/
print(bag,bag_len);
memset(bag,'\0',bag_len);
memset(half_bag,'\0',1024);
*half_len = 0;
}else{
return -1;/*close socket,error bag*/
}
}
}
/*处理分包,一个包取出,剩下的继续解析*/
int Get_Bag(char *buffer,int len,char *bag,int *bag_len,char *half_bag,int *half_len)
{
int i = 0;
if(*half_len != 0){
if((buffer[0] == 0x7e) && (buffer[1] == 0x7e)){
printf("buffer[0]:%x\n",buffer[0]);
half_bag[*half_len] = buffer[0];
*bag_len = *half_len + 1;
memcpy(bag,half_bag,*bag_len);
return S4;/*拼成一个完整包,下半包只有一个7e*/
}
}
if(buffer[i] == 0x7e){/*7e 开头*/
bag[0] = buffer[0];
i ++;
while(i < len){
bag[i] = buffer[i];
/*一个完整的包,而且长度刚好*/
if((buffer[i] == 0x7e) && (i + 1 == len)){
*bag_len = len;
return S1;
}else if((i+1 == len) && (buffer[i] != 0x7e)){
memcpy(half_bag,buffer,len);
*half_len = len;
return S3;/*7e开头的半包*/
}else if((buffer[i] == 0x7e) && (i < len)){/*一个完整的包,还有数据*/
*bag_len = i + 1;
return S2;
}
i ++;
}
}else{/*非7e开头*/
printf("非7e开头\n");
if(*half_len != 0){
char tmp[1024] = {'\0'};
while(i < len){
tmp[i] = buffer[i];
if((buffer[i] == 0x7e) && ((i + 1) == len)){
memcpy(half_bag+(*half_len),tmp,len);
memcpy(bag,half_bag,(*half_len)+len);
*bag_len = (*half_len) + len;
memset(half_bag,'\0',(*bag_len));
return 1;
}else if((buffer[i] == 0x7e) && (i + 1 < len)){
memcpy(half_bag+(*half_len),tmp,i+1);
memcpy(bag,half_bag,(*half_len) + i + 1);
*bag_len = (*half_len) + i + 1;
memset(half_bag,'\0',*bag_len);
return 5;/*拼一个完整的包,还有数据*/
}else if((i + 1 == len) && (buffer[i] != 0x7e)){
printf("错误包\n");
return SFAULT;/*错误包*/
}
i++;
}
}else{
printf("错误包\n");
return SFAULT;
}
}
}
楼主的开发环境:
系统 centos6.4 编译器 gcc4.4.7
以上代码楼主亲测可用,这里就不附测试结果。楼主觉得还有很多不合理的地方,暂时不知道怎么解决。比如:如果一次接受数据没有半包,而这一次接收数据之后不以7e开头的,楼主就把数据包丢掉了。这里可能会丢掉太多有用的包,所以比较坑。希望大家能够提出意见,不喜请大喷,喷喷更健康。
转载于:https://blog.51cto.com/wonderwander/1620119