当前位置: 首页> 健康> 科研 > 餐饮小程序开发_临沂专业网站建设设计公司_网络推广工作好做不_手游cpa推广平台

餐饮小程序开发_临沂专业网站建设设计公司_网络推广工作好做不_手游cpa推广平台

时间:2025/7/11 18:22:16来源:https://blog.csdn.net/2301_76460223/article/details/146507884 浏览次数:0次
餐饮小程序开发_临沂专业网站建设设计公司_网络推广工作好做不_手游cpa推广平台

一、TCP粘包简介

        使用TCP 协议进行数据传输时,多个数据包被连续存储于缓存中,在对数据包进行读取时由于无法确定发送方的发送边界,而采用某一估测值大小来进行数据读取,使得发送方发送的若干个数据包到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
从上图中可以看出粘包主要分为两种情况:
        多个完整的数据包粘在一起
        一个完整的数据包被拆分成两部分与其它完整包粘在一起
TCP粘包问题会影响到有数据结构的数据包,会导致数据包解析出现问题.

二、TCP粘包原因分析

TCP 协议是面向连接的、可靠的、基于字节流的传输层通信协议出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。

粘包的根本原因

原因说明
TCP是字节流协议数据像水流一样没有固定边界,应用层需自行划分。
Nagle算法默认开启,会合并多个小数据包发送(减少网络拥塞)。
缓冲区机制发送缓冲区积累到一定大小或超时才会发送;接收缓冲区可能一次性读取多个包。

发送方引起的粘包是由TCP协议本身造成的

        TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP 会根据优化算法 (Nagle) 把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。

发送方一次性发送的数据大于 MTU ,则会发生拆包,将字节流进行切片分成多个包进行发送
接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象
        这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。

三、TCP解决方案

方式一 : 使用定长数据包,每次必须要读取固定长度的数据,(适用于数据长度固定的情景)

方式二:使用数据长度+数据的方式,先接收数据长度,再根据长度接收数据,(适用于数据长读度不固定的情景)

三, TCP粘包使用方式二解决实现

发送时,将数据长度+数据 作为数据包的内容,整体发送:
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main(int argc,char* argv[])
{
// 创建socket套接字的
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1){
perror("socket");
exit(EXIT_FAILURE);}
// 与服务器进行连接
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&(addr.sin_addr));
int ret = connect(sockfd,(const struct sockaddr*)&addr,sizeof(struct
sockaddr_in));
if(ret==-1 ){
perror("connect");
close(sockfd);
exit(EXIT_FAILURE);}
// 向服务器端发送数据
char buf[]={"hello tcp!"};
for(int i=0;i<1000;i++){
int ret;
// 1、计算数据长度
int length = strlen(buf);
// 2、开辟空间
char* p_buf=(char*)malloc(length+4);// 使用整型来保存数据长度
// 3、对指定的空间赋值
memcpy(p_buf,&length,4);
memcpy(p_buf+4,buf,length);
//4、发送数据
ret = send(sockfd,p_buf,length+4,0);
if(ret == -1){
perror("send failed");
exit(EXIT_FAILURE);}}
close(sockfd);
return 0;
}

服务器端:

接收时,先接收数据长度,再根据数据长度固定接收数据:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#define BACKLOG 20
int main(int argc,char* argv[])
{
if(argc !=3){
fprintf(stderr,"%s ip port.\n",argv[0]);
exit(EXIT_FAILURE);}
// 创建socket
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1){
perror("socket failed.");
exit(EXIT_FAILURE);}
// 绑定ip+端口号
struct sockaddr_in addr;
int addrlen = sizeof(struct sockaddr_in);
bzero(&addr,addrlen);
addr.sin_family = AF_INET;
addr.sin_port=htons(atoi(argv[2]));
inet_aton(argv[1],&(addr.sin_addr));
int ret = bind(sockfd,(const struct sockaddr*)&addr,addrlen);
if(ret == -1){
perror("bind failed");
exit(EXIT_FAILURE);}
// 建立监听队列
ret = listen(sockfd,BACKLOG);
if(ret == -1){
perror("listen failed");
exit(EXIT_FAILURE);}
//建立连接
struct sockaddr_in client_addr;
int client_addrlen = sizeof(struct sockaddr_in);
int cfd = accept(sockfd,(struct
sockaddr*)&client_addr,&client_addrlen);
if(cfd == -1){
perror("accept failed.");
exit(EXIT_FAILURE);}
// 打印客户端的信息
ssize_t rbytes = 0;
char buf[1024]={0};
while(1){
int length = 0;
int total_received = 0;//已经接收的字节数
// 1、先接收数据长度
rbytes = recv(cfd,&length,4,0);
if(rbytes == -1){
perror("recv data length failed");
exit(EXIT_FAILURE);}
memset(buf,0,sizeof(buf));
// 接收客户端的消息(注意:使用新的文件标识符)
while(1){
printf("length=%d\n",length);
rbytes = recv(cfd,buf+total_received,lengthtotal_received,0);
if(rbytes==-1){
perror("server recv failed.");
exit(EXIT_FAILURE);}
else if(rbytes == 0){
fprintf(stderr,"stream socket shutdown.\n");
exit(EXIT_FAILURE);}
else{
total_received += rbytes;
if(total_received == length)//接收完成{
printf("recv message: %s\n",buf);
break;}}}
// 休眠一秒,发送数据比接收数据快
sleep(1);}
return 0;
}
运行结果:
关键字:餐饮小程序开发_临沂专业网站建设设计公司_网络推广工作好做不_手游cpa推广平台

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: