tcp 封包与解包

粘包

连续两次send两端数据data1和data2,接收端有几种接收情况

1、先data1,在data2

2、先data1的部分数据,在data1余下的部分以及data2

3、先data1的全部和data2的部分,然后接受data2的余下数据

4、一次性接受data1和data2的全部数据

234就是长说的“粘包”,就需要把接受的数据进行拆包,拆成一个个独立的数据包,而为了拆包必须在发送端进行封包

原因

1、由Nagle算法造成发送端的粘包,当提交一段数据给TCP发送时,TCP不立刻发送此段数据,而是等待一小短时间,看看等待期间是否还有要发送的数据,有的话就一次性发送。上述3、4就是这个原因。

2、接收端接受不及时造成的接收端粘包,TCP会把接受到的数据存在自己的缓冲区中,然后通知应用层取数据。当应用层取数据不及时,就会造成缓冲区存放很多数据

解决方法

最初就是在两次send之间调用sleep来休眠一小段时间,但是效率大大降低,而且也不可靠。

对数据包进行封包和拆包就能解决这个问题

封包和拆包

封包

给一段诗句加上包头,这样数据包就变成包头+包体两部分,包头其实就是大小固定的结构体,其中有个结构体变量表示包体的长度,这是个很重要的变量,其他的结构体成员可以根据自己定义。

拆包

利用底层的缓冲区进行拆包,由于TCP也维护了一个缓冲区,可以利用TCP的缓冲区进行缓存发送的数据,这样就不用为每一个连接分配一个缓冲区了,利用缓冲区来拆包,循环接受包头给出的数据,直到收购位置。

解决粘包

为了解决粘包问题,通常会在发送内容前,加上发送内容的长度,所以对方会先收到4Byte,解析获得接下来需要接受的长度,在进行收包

发送与接受一个字符串

客户端

发送的数据前面4个字节表示这个字符串的大小

	ssize_t writeLen;
    char *sendMsg = "0123456789";
    int tLen=strlen(sendMsg);
    printf("tLen:%d\n" ,tLen);
    int iLen=0;
    char * pBuff= new char [100];
    *(int*)(pBuff+iLen)= htonl(tLen);
    iLen+=sizeof( int);
    memcpy(pBuff+iLen,sendMsg,tLen);
    iLen+=tLen;
    writeLen= MySend(connfd, pBuff, iLen);

把字符串的长度转换成网络字节序,由于发送的内容是字符串,无需转换成网络字节序,直接加到后面就行,发送时间,由于事先不知道发送的诗句有多大,写了下面的函数,一次发不完,可以接着发送,直到发完指定的长度为止

int MySend( int iSock, char * pchBuf, size_t tLen){
        int iThisSend;
        unsigned int iSended=0;
        if(tLen == 0)
               return(0);
        while(iSended<tLen){
              do{
                     iThisSend = send(iSock, pchBuf, tLen-iSended, 0);
              } while((iThisSend<0) && (errno==EINTR));
               if(iThisSend < 0){
                      return(iSended);
              }
              iSended += iThisSend;
              pchBuf += iThisSend;
       }
        return(tLen);
}

服务端

首先需要先接受4个字节,并转换成主机序,才知道接下来要接受多少的数据

	ssize_t readLen = MyRecv(acceptfd, recvMsg, sizeof( int));
    if (readLen < 0) {
       printf("read failed\n" );
       return -1;
    }
    int len=( int)ntohl(*( int*)recvMsg);
    printf("len:%d\n",len);
    readLen = MyRecv(acceptfd, recvMsg, len);

不知道会接受多少字节,写一个函数,用于循环接受,直到接受完指定数量为止。

int MyRecv( int iSock, char * pchBuf, size_t tCount){
        size_t tBytesRead=0;
        int iThisRead;
        while(tBytesRead < tCount){
              do{
                     iThisRead = read(iSock, pchBuf, tCount-tBytesRead);
              } while((iThisRead<0) && (errno==EINTR));
              if(iThisRead < 0){
                      return(iThisRead);
              }else if (iThisRead == 0)
                      return(tBytesRead);
              tBytesRead += iThisRead;
              pchBuf += iThisRead;
       }
}

发送与接受一个结构体

结构体

struct Header {
        int num ;//包id
        int index ;//学生编号
};
struct PkgContent {
        char sex ;//性别
        int score ;//分数
        char address [100];//地址
		int age;
};
struct Pkg {
        Header head;
        PkgContent content ;
};

客户端

	Pkg mypkg;
    mypkg.head.num=1;
    mypkg.head.index=10001;
    mypkg.content.sex='m';
    mypkg.content.score=90;
    char * temp="guangzhou and shanghai";
    strncpy(mypkg.content.address,temp,sizeof(mypkg.content.address));
    mypkg.content.age=18;
	ssize_t writeLen;
	//计算结构提的大熊啊
    int tLen=sizeof(mypkg);
    printf("tLen:%d\n" ,tLen);
    int iLen=0;
    char * pBuff= new char [1000];
    //对每个元素进行网络字节序的转换,然后放到一个char数组
    *(int*)(pBuff+iLen)= htonl(tLen);
    iLen+=sizeof( int);
    *(int*)(pBuff+iLen)= htonl(mypkg.head.num);
    iLen+=sizeof( int);
    *(int*)(pBuff+iLen)= htonl(mypkg.head.index);
    iLen+=sizeof( int);
    memcpy(pBuff+iLen,&mypkg.content.sex,sizeof( char));
    iLen+=sizeof( char);
    *(int*)(pBuff+iLen)= htonl(mypkg.content.score);
    iLen+=sizeof( int);
    memcpy(pBuff+iLen,mypkg.content.address,sizeof(mypkg.content.address));
    iLen+=(sizeof(mypkg.content.address));
    *(int*)(pBuff+iLen)= htonl(mypkg.content.age);
    iLen+=sizeof( int);
    
	//发送代码
    writeLen= MySend(connfd, pBuff, iLen);
    if (writeLen < 0) {
       printf("write failed\n" );
       close(connfd);
       return 0;
    }

服务端

先接受4个字节的包,然后转换成主机序,得到接下来需要接受的长度

	ssize_t readLen = MyRecv(acceptfd, recvMsg, sizeof(int));
    if (readLen < 0) {
       printf("read failed\n" );
       return -1;
    }
    int len=(int)ntohl(*(int*)recvMsg);
    printf("len:%d\n",len);
    readLen = MyRecv(acceptfd, recvMsg, len);
    if (readLen < 0) {
       printf("read failed\n" );
       return -1;
    }

然后将每个元素解析出来

	memcpy(&RecvPkg.head.num , pBuff + iLen, sizeof( int));
    iLen += sizeof(int);
    RecvPkg. head. num = ntohl(RecvPkg.head.num);
    printf("RecvPkg.head.num:%d\n" ,RecvPkg.head.num);
    memcpy(&RecvPkg.head.index , pBuff + iLen, sizeof( int));
    iLen += sizeof(int);
    RecvPkg. head. index = ntohl(RecvPkg.head.index);
    printf("RecvPkg.head.index:%d\n" ,RecvPkg.head.index);
    memcpy(&RecvPkg.content.sex , pBuff + iLen, sizeof( char));
    iLen += sizeof(char);
    printf("RecvPkg.content.sex:%c\n" ,RecvPkg.content.sex);
    memcpy(&RecvPkg.content.score , pBuff + iLen, sizeof( int));
    iLen += sizeof(int);
    RecvPkg. content.score = ntohl(RecvPkg. content.score );
    printf("RecvPkg.content.score:%d\n" ,RecvPkg.content.score);
    memcpy(&RecvPkg.content.address, pBuff + iLen, sizeof(RecvPkg.content.address ));
    iLen += sizeof(RecvPkg.content.address);
    printf("RecvPkg.content.address:%s\n" ,RecvPkg.content.address);
    memcpy(&RecvPkg.content.age , pBuff + iLen, sizeof( int));
    iLen += sizeof(int);
    RecvPkg.content.age = ntohl(RecvPkg.content.age );
    printf("RecvPkg.content.age:%d\n" ,RecvPkg.content.age);

现在可以用protobuff等自动生成解析数据的函数

粉丝福利, 免费领取C/C++ 开发学习资料包、技术视频/项目代码,1000道大厂面试题,内容包括(C++基础,网络编程,数据库,中间件,后端开发/音视频开发/Qt开发/游戏开发/Linuxn内核等进阶学习资料和最佳学习路线)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 易语言是一种可视化编程语言,用于开发Windows平台的应用程序。封解包工具是指用于将数据进行打和拆的工具,主要用于网络编程中数据的传输和通信。 易语言中有一些第三方库和插件可以用于实现TCP解包的功能。这些库和插件提供了一些函数和方法,方便开发人员在易语言中进行数据的封解包TCP是将数据按照一定的格式进行打,以便于在网络上进行传输。常用的封格式有固定长度封、变长封和自定义格式封等。使用易语言进行TCP时,可以使用这些第三方库和插件中提供的函数,按照指定的封格式将数据进行打TCP解包是将封后的数据拆分成可识别的数据。解包操作与封相反,通过读取封中的长度字段和标识字段等信息,将封还原为原始数据。易语言中的TCP解包工具可以根据封格式的规则进行解包操作,并将解包后的数据返回给开发人员使用。 使用易语言进行TCP解包的工具可以帮助开发人员简化数据传输的过程,提高开发效率和数据传输的安全性。通过这些工具,开发人员可以方便地进行数据的封解包操作,从而更好地实现网络通信功能。 ### 回答2: 易语言是一种编程语言,它可以用于开发Windows系统下的应用程序。TCP解包工具是易语言提供的一个工具,用于处理TCP协议传输的数据。 在网络通信中,数据的传输是以数据的形式进行的。TCP解包工具可以帮助程序员处理从网络接收到的数据,以及将要发送的数据转换为合适的格式进行传输。 封指的是将数据转换为特定格式的过程。TCP解包工具可以将数据按照协议要求的格式进行封装,例如添加报文头部、计算校验和等操作,以便正确地发送给对方。 解包指的是将接收到的数据进行解析的过程。TCP解包工具可以按照协议规定的格式解析数据,并提取出其中的有效信息,以便程序能够正确地处理这些数据。 TCP解包工具可以帮助程序员简化网络通信的开发过程,使得开发人员能够更加专注于实现业务逻辑。它提供了一套封解包的接口和函数库,开发者只需要调用相应的函数来完成数据的封解包操作即可。 总之,易语言TCP解包工具是易语言提供的一个工具,用于处理TCP协议传输的数据,使开发者能够更加方便地进行网络通信的开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值