TCP粘包分析与处理

简介: TCP粘包现象TCP粘包通俗来讲,就是发送方发送的多个数据包,到接收方后粘连在一起,导致数据包不能完整的体现发送的数据。TCP粘包原因分析导致TCP粘包的原因,可能是发送方的原因,也有可能是接受方的原因。

TCP粘包现象

TCP粘包通俗来讲,就是发送方发送的多个数据包,到接收方后粘连在一起,导致数据包不能完整的体现发送的数据。

TCP粘包原因分析

导致TCP粘包的原因,可能是发送方的原因,也有可能是接受方的原因。

发送方

由于TCP需要尽可能高效和可靠,所以TCP协议默认采用Nagle算法,以合并相连的小数据包,再一次性发送,以达到提升网络传输效率的目的。但是接收方并不知晓发送方合并数据包,而且数据包的合并在TCP协议中是没有分界线的,所以这就会导致接收方不能还原其本来的数据包。

接收方

TCP是基于“流”的。网络传输数据的速度可能会快过接收方处理数据的速度,这时候就会导致,接收方在读取缓冲区时,缓冲区存在多个数据包。在TCP协议中接收方是一次读取缓冲区中的所有内容,所以不能反映原本的数据信息。

解决TCP粘包

分析了产生TCP粘包的原因之后,针对发生的原因,针对性的采取解决方法。

禁用Negle算法

因为TCP协议采用Negle算法,导致粘包。所以可以禁用Nagle算法。

const char chOpt = 1;
int nErr = setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, &chOpt, sizeof(char)); if(nErr == -1) { TRACE( "setsockopt() error\n", WSAGetLastError()); return ; }

这种方法虽然能一定程度上解决TCP粘包,但是并不能完全解决问题。因为接收方也是可能造成粘包的原因,这种方法只是发送方有效。而且禁用Nagle算法,一定程度上使TCP传输效率降低了。所以,这并不是一种理想的方法。

PUSH标志

PUSH是TCP报头中的一个标志位,发送方在发送数据的时候可以设置这个标志位。该标志通知接收方将接收到的数据全部提交给接收进程。这里所说的数据包括与此PUSH包一起传输的数据以及之前就为该进程传输过来的数据。
当Server端收到这些数据后,它需要立刻将这些数据提交给应用层进程,而不再等待是否还有额外的数据到达。
设置PUSH标志也不能完全解决TCP粘包,只是降低了接收方粘包的可能性。实际上现在的TCP协议栈基本上都可以自行处理这个问题,而不是交给应用层处理。所以设置PUSH标志,也不是一种理想的方法。

自定协议

自定协议,将数据包分为了封包和解包两个过程。在发送方发送数据时,对发送的数据进行封包操作。在接收方接收到数据时对接收的数据包需要进行解包操作。
自定协议时,封包就是为发送的数据增加包头,包头包含数据的大小的信息,数据就跟随在包头之后。当然包头也可以有其他的信息,比如一些做校验的信息。这里主要讨论TCP粘包的问题,所以不考虑其他的。

发送方封包

PACKAGE_HEAD pPackageHead; //PACKAGE_HEAD 包头结构体
char PackageHead[1024];
int headLen = sizeof(PACKAGE_HEAD); int packgeContextLen = strlen(packageContext); //packageContext 发送的数据 pPackageHead->nDataLen = packgeContextLen; //包的大小 char *packge = (char*)malloc(headLen + packgeContextLen); //包的内存分配 memset(packge, 0, headLen + packgeContextLen); char *packgeCpy = (char*)memcpy(packge, (char*)&pPackageHead, headLen);//拷贝包头 packgeCpy += headLen; packge = (char*)memcpy(packgeCpy, (char*)&packageContext, packgeContextLen);//拷贝包内容 int ret = 0; ret = send(m_hSocket, packge, headLen + packgeContextLen, 0); //发送包 if (ret == SOCKET_ERROR || ret == 0) { return ret; }

接收方解包

char PackageHead[1024];
char PackageContext[1024*20]; int len; PACKAGE_HEAD *pPackageHead; //PACKAGE_HEAD 包头结构体 while( m_bClose == false ) { memset(PackageHead, 0, sizeof(PACKAGE_HEAD)); len = ReceiveSize(m_TcpSock, (char*)PackageHead, sizeof(PACKAGE_HEAD)); //接收包头 if( len == SOCKET_ERROR ) { break; } if(len == 0) { break; } pPackageHead = (PACKAGE_HEAD *)PackageHead; memset(PackageContext,0,sizeof(PackageContext)); if(pPackageHead->nDataLen>0) //根据包头中的数据长度,接收数据 { len = ReceiveSize(m_TcpSock, (char*)PackageContext,pPackageHead->nDataLen); } }

接收指定长度的数据函数

//接收指定长度的数据
int ReceiveSize(SOCKET m_hSocket, char* strData, int gLen) { if(strData == NULL) return ERR_BADPARAM; char *p = strData; int len = gLen; int ret = 0; int returnlen = 0; while( len > 0) { ret = recv( m_hSocket, p+(iLen-len), iLen-returnlen, 0); if (ret == SOCKET_ERROR || ret == 0) { return ret; } len -= ret; returnlen += ret; } return returnlen; }

这样就可以达到解决TCP粘包的问题。在实际使用中包头还带有更多的信息,而且包尾可能还会带上分隔符,在redis、FTP中就是这样处理的。

UDP不存在粘包

由于UDP不是面向‘流’的,而且UDP是具有消息边界的。也就是说UDP的发送的每一个数据包都是独立的。所以UDP并不存在粘包的问题。

目录
相关文章
|
存储 easyexcel Java
EasyExcel教程
EasyExcel教程
17232 0
|
开发工具 git
如何在vscode编辑器中实时查看代码git记录(被谁修改、自己什么时候修改)
如何在vscode编辑器中实时查看代码git记录(被谁修改、自己什么时候修改)
8740 0
如何在vscode编辑器中实时查看代码git记录(被谁修改、自己什么时候修改)
|
人工智能 Rust 开发者
【MCP教程系列】使用Python在阿里云百炼创建基于UVX的MCP服务完整指南
本文介绍如何使用基于uvx工具链的Python项目,结合阿里云百炼平台实现小红书文案审核助手的MCP服务开发与部署。首先通过安装uv工具初始化项目并配置虚拟环境,编写server.py文件调用qwen-plus模型完成内容审核功能。随后将项目打包上传至PyPI,供全球开发者访问。接着在阿里云百炼平台上配置并部署该MCP服务,解决可能的依赖问题。最后,在智能体应用中引入此MCP服务进行测试验证,确保其正常运行。
4673 0
|
监控 安全 API
构建坚不可摧的防线:全方位保障API接口数据安全
在数字化时代,API作为系统间数据沟通的桥梁,其安全性至关重要。本文系统解析API安全的四大基石:身份认证、授权管理、数据完整性与机密性,并深入探讨HTTPS加密、强认证机制、精细授权、数据保护及纵深防御等关键技术实践,帮助企业构建全面的API安全体系,防范数据泄露与攻击风险,保障数据传输安全与业务稳定运行。
|
负载均衡 安全 网络协议
|
存储 人工智能 缓存
超越单智能体!原生多Agent系统开发指南(附完整源码)
本文深入探讨多智能体系统的核心原理与工程实践,解析其模块化、错误隔离与解释性优势,并通过实战示例展示如何构建多智能体新闻生成器,助力AI协作应用开发。
3729 0
|
存储 数据管理 Apache
Doris建表分桶选择与优化建议
Apache Doris 中的分桶(Bucketing)是提升查询性能的重要优化手段。通过合理选择分桶列和分桶数,可提高数据并行处理能力与局部性。建议选用高基数、高频查询列作为分桶列,结合数据量与集群规模设置分桶数,推荐使用自动分桶(BUCKETS AUTO)。分桶策略包括哈希分桶与范围分桶,适用于不同场景。合理分桶可优化查询性能、导入效率与资源利用率,建议结合业务特征测试验证最佳方案。
1198 0
|
SQL 关系型数据库 MySQL
删库,误清数据怎么办?MySQL数据恢复指南
相信很多同学在面对线上数据库时都畏手畏脚,即使这样都难免手滑,一不小心手一抖就将数据或者是表,库删除。当然一些注重规范的公司,不会给开发人员删除表或者是库的权限,但误删数据是常有的事,那么这种情况发生,我们改怎么办呢?跑路?哈哈,当然删库跑路是句玩笑话,本文就为大家介绍一些数据误删除恢复的办法。
4516 0
|
机器学习/深度学习 人工智能 PyTorch
从零开始:如何训练自己的AI模型
### 从零开始:如何训练自己的AI模型 训练AI模型如同培养新生儿,需耐心与技巧。首先明确目标(如图像识别、自然语言处理),选择框架(TensorFlow、PyTorch)。接着收集并预处理数据,确保多样性和准确性。然后设计模型结构,如卷积神经网络(CNN),并通过代码实现训练。训练后评估模型性能,调优以避免过拟合。最后部署模型至实际应用。通过猫狗分类器案例,掌握关键步骤和常见问题。训练AI模型是不断迭代优化的过程,实践才能真正掌握精髓。
|
分布式计算 数据库
Mapreduce中的Mapper&reducer
【9月更文挑战第19天】在 MapReduce 框架中,Mapper 和 Reducer 是处理大规模数据集的关键组件。Mapper 负责将输入数据分割成键值对,而 Reducer 则对这些键值对进行汇总处理,生成最终结果。两者通过并行处理和分布式计算协同工作,Mapper 将数据转换为键值对,Reducer 对相同键的值进行聚合。开发人员需实现相应接口并编写定制逻辑,以充分利用框架优势,处理大规模数据集并获得有价值的结果。
971 8