拆包粘包问题的解决方案

拆包粘包处理

情况1.传来的数据刚好是一个整包

拆包粘包问题的解决方案
此时的数据刚好能够传递给上层,于是直接给packet,而offset(即还差多少)设置为0

情况2.拆包

拆包粘包问题的解决方案

情况3.粘包

拆包粘包问题的解决方案
这样处理过后又回到了拆包的情况

代码复现

准备工作

头文件的编写,调试文件的编写

head.h

包含必要的系统头文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>

color.h

调试信息带颜色便于分辨,看起来也赏心悦目一点

#ifndef _COLOR_H
#define _COLOR_H
#define NONE  "\e[0m"       //清除颜色,即之后的打印为正常输出,之前的不受影响
#define BLACK  "\e[0;30m"  //深黑
#define L_BLACK  "\e[1;30m" //亮黑,偏灰褐
#define RED   "\e[0;31m"    //深红,暗红
#define L_RED  "\e[1;31m"   //鲜红
#define GREEN  "\e[0;32m"   //深绿,暗绿
#define L_GREEN   "\e[1;32m"//鲜绿
#define BROWN "\e[0;33m"    //深黄,暗黄
#define YELLOW "\e[1;33m"   //鲜黄
#define BLUE "\e[0;34m"     //深蓝,暗蓝
#define L_BLUE "\e[1;34m"   //亮蓝,偏白灰
#define PINK "\e[0;35m"     //深粉,暗粉,偏暗紫
#define L_PINK "\e[1;35m"   //亮粉,偏白灰
#define CYAN "\e[0;36m"     //暗青色
#define L_CYAN "\e[1;36m"   //鲜亮青色
#define GRAY "\e[0;37m"     //灰色
#define WHITE "\e[1;37m"    //白色,字体粗一点,比正常大,比bold小
#define BOLD "\e[1m"        //白色,粗体
#define UNDERLINE "\e[4m"   //下划线,白色,正常大小
#define BLINK "\e[5m"       //闪烁,白色,正常大小
#define REVERSE "\e[7m"     //反转,即字体背景为白色,字体为黑色
#define HIDE "\e[8m"        //隐藏
#define CLEAR "\e[2J"       //清除
#define CLRLINE "\r\e[K"    //清除行
#endif

debug.h

调试的时候加入-D DBG选项即可显示调试信息

#ifdef DBG
#define DEBUG(fmt,args...) printf(fmt,##args)
#else
#define DEBUG(fmt,args...)
#endif

datatype.h

用于定义接收文件的数据类型,结构体

struct filePacket{
        char name[50];//文件名
        uint64_t size;//文件大小
        char buff[4096];//文件块,故意设置成超过1460字节,这样就会被拆包
};//记得结构体后面一定要加 ' ; ' 不然编译会报错说哪里哪里缺少一个 ' ; '

函数编写

m_socket.c

套接字的创建和客户端的连接

#include "head.h"
#include "datatype.h"
#include "m_socket.h"
int socket_create(int port){
        int sockfd;
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){
                return -1;
        }
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port=htons(port);
        addr.sin_addr.s_addr=inet_addr("0.0.0.0");
        if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr))<0){
                return -1;
        }
        if(listen(sockfd,8)<0)return -1;
        return sockfd;
}
int socket_connect(const char* ip,int port){
        int sockfd;
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){
                return -1;
        }
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port=htons(port);
        addr.sin_addr.s_addr=inet_addr(ip);
        if(connect(sockfd,(struct sockaddr*)&addr,sizeof(addr))<0){
                return -1;
        }
        return sockfd;
}

filetransfer.c

文件的接收和发送函数

#include "head.h"
#include "datatype.h"
#include "color.h"
#include "debug.h"
int send_file(int sockfd,const char * name){
        DEBUG(BLUE"<debug>: "NONE"file name:%s\n",name);
        FILE* fp;
        if((fp=fopen(name,"rb"))==NULL){
                DEBUG(RED"<error>: "NONE"fopen failed\n");
                return -1;
        }
        struct filePacket packet;
        size_t spacket = sizeof(packet);
        size_t sbuff = sizeof(packet.buff);
        bzero(&packet,spacket);

        //start get the file len
        fseek(fp,0L,SEEK_END);
        packet.size = ftell(fp);
        fseek(fp,0L,SEEK_SET);
        //end get the file len

        //start 获取文件名
        char filename[50];
        char * p ;
        p = strrchr(name,'/');
        if(p==NULL){
                strcpy(filename,name);
                printf("file name :%s\n",filename);
        }else{
                strcpy(filename,p+1);
        }
        //end 获取文件名

        strcpy(packet.name,filename);
        DEBUG(YELLOW"<debug>: "NONE"file name = %s,file size = %ld\n",packet.name,packet.size);

        while(!feof(fp)){

                size_t rsize = fread(packet.buff,1,sbuff,fp);
                if(ferror(fp)){
                        DEBUG(RED"<error>: "NONE"read occurs error\n");
                        return -1;
                }
                ssize_t ssize = send(sockfd,(void*)&packet,spacket,0);
                memset(packet.buff,0,sbuff);
                DEBUG(YELLOW"<debug>: "NONE"ssize = %ld,rsize=%ld\n",ssize,rsize);
        }
        return 0;

}
int recv_file(int sockfd){
        struct filePacket packet_pre,packet_temp,packet;
        size_t spacket = sizeof(struct filePacket);
        bzero(&packet_pre,spacket);
        bzero(&packet_temp,spacket);
        bzero(&packet,spacket);

        int offset=0;
        int count=0;
        uint64_t file_size,total_size=0;
        size_t buff_size;
        size_t wsize;
        FILE *fp;
        while(1){
                if(offset){
                        //something left in the packet_temp
                        memcpy((void*)(&packet),&packet_pre,offset);
                }
                memset(packet_pre.buff,0,sizeof(packet_pre.buff));

                memset(packet_temp.buff,0,sizeof(packet_temp.buff));
                //receive a full packet
                while(1){
                        ssize_t rsize = recv(sockfd,(void*)&packet_temp,spacket,0);
                        printf("rsize=%ld\n",rsize);
                        if(rsize<=0)break;

                        if((offset+rsize)==spacket){
                                DEBUG(BLUE"<debug>: "NONE"receive full packet\n");
                                memcpy((char*)&packet+offset,&packet_temp,rsize);
                                offset=0;
                                break;
                        }else if((offset+rsize)<spacket){
                                DEBUG(YELLOW"<debug>: "NONE"receive less packet\n");
                                memcpy((char*)&packet+offset,&packet_temp,rsize);
                                offset+=rsize;
                        }else if((offset+rsize)>spacket){
                                DEBUG(L_PINK"<debug>: "NONE"receive more packet\n");
                                int need = spacket-offset;
                                memcpy((char*)(&packet+offset),(&packet_temp),need);
                                memcpy((char*)(&packet_pre),&packet_temp+need,rsize-need);
                                offset = rsize - need;
                                break;
                        }

                }
                //if 1st ,extract filename ,size
                if(count==0){
                        char path [512]={0};
                        sprintf(path,"%s/%s","./data",packet.name);
                        DEBUG(YELLOW"<debug>: "NONE"packet.name = %s,packet.size = %ld\n",packet.name,packet.size);
                        file_size = packet.size;
                        if((fp=fopen(path,"wb"))==NULL){
                                DEBUG(RED"<error>: "NONE"fopen error occurs\n");
                                return -1;
                        }
                }
                count++;
                buff_size = sizeof(packet.buff);
                if(file_size-total_size>buff_size){
                        wsize = fwrite(packet.buff,1,buff_size,fp); 
                }else{
                        wsize = fwrite(packet.buff,1,file_size-total_size,fp);
                }
                memset(packet.buff,0,buff_size);
                memset(packet_temp.buff,0,sizeof(packet_temp.buff));
                total_size += wsize;
                DEBUG(L_PINK"<debug>: "NONE"total_size = %ld,wsize = %ld,file_size=%ld\n",total_size,wsize,file_size);
                if(total_size>=file_size){
                        DEBUG(YELLOW"<debug>: "NONE"file transfer complete\n");
                        break;
                }
        }
        fclose(fp);
        return 0;
}

客户端和服务端的编写

这个很简单,服务端加一个fork,然后recv_file,客户端直接send_file就行了

服务端

#include "head.h"
#include "m_socket.h"
#include "filetransfer.h"
void check(int argc, int correctValue,char * proname);
int main(int argc,char **argv){
        check(argc,2,argv[0]);

        int server_listen,sockfd;
        if((server_listen=socket_create(atoi(argv[1])))<0){
                perror("socket_create");
                exit(1);
        }
        printf("socket listening on port : %d\n",server_listen);
        while(1){
                if((sockfd=accept(server_listen,NULL,NULL))<0){
                        perror("accept");
                        exit(1);
                }
                pid_t pid;
                if((pid=fork())<0){
                        perror("fork");
                        exit(1);
                }
                if(pid){
                        close(sockfd);
                        continue;
                }
                close(server_listen);
                int ret = recv_file(sockfd);
                if(ret<0){
                        perror("recv_file");
                }
                break;
        }
        return 0;
}
void check(int argc, int correctValue,char * proname){
        if(argc!=correctValue){
                fprintf(stderr,"USAGE:%s is not correct!\n",proname);
                exit(1);
        }
}

客户端

#include "head.h"
#include "m_socket.h"
#include "filetransfer.h"
int main(int argc , char ** argv){
        int sockfd;
        if((sockfd=socket_connect(argv[1],atoi(argv[2])))<0){
                perror("connect");
                exit(1);
        }
        send_file(sockfd,argv[3]);
        return 0;
}

编写脚本直接编译

gcc server.c m_socket.c filepackage.c -o server -D DBG
gcc client.c m_socket.c filepackage.c -o client -D DBG
上一篇:【Java集合】ConcurrentHashMap(1.7)源码解析


下一篇:第三次SDN实验