昨天问了某位美女姐姐一个逻辑问题。由于楼主嘴太笨,语言表达实在不怎么的。到最后也没有解释清楚,最后放弃口头叙述,特写此博客,供美女姐姐参看。要是再不懂,说明我的文字表达也不怎么的,不知到美女姐姐怎么看。


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开头的,楼主就把数据包丢掉了。这里可能会丢掉太多有用的包,所以比较坑。希望大家能够提出意见,不喜请大喷,喷喷更健康。