突破编程_C++_网络编程(Windows 套接字(处理 TCP 粘包问题))
1 TCP 协议与粘包问题概述
1.1 TCP 粘包的产生原因
TCP粘包问题的产生原因涉及多个方面,主要的原因如下:
-
首先,发送方在发送数据时,由于TCP协议为提高传输效率而采用的Nagle算法,可能会将多个小数据包合并成一个大数据包进行发送。这种合并操作在TCP协议中是没有明确的分界线的,因此接收方在接收数据时无法准确区分原本的数据包,导致粘包现象的产生。
-
其次,接收方的缓冲区大小设置也可能影响粘包问题的发生。如果接收方的缓冲区大小设置不合理,多个小数据包可能会因为缓冲区大小限制而粘在一起传输,形成一个大数据包发送。这种情况下,接收方同样无法准确区分原始的数据包,从而产生粘包问题。
-
此外,网络状况的不稳定也可能导致TCP粘包问题。例如,网络延迟和抖动可能导致数据包到达顺序与发送顺序不一致,从而破坏了数据包的边界。当数据包在网络传输过程中发生乱序或丢失时,接收方在重新组装数据包时可能会出现错误,导致粘包现象的发生。
-
最后,多个应用程序利用相同的TCP连接并发发送数据也可能引发粘包问题。由于TCP本身是流式协议,无法识别消息边界,因此当多个应用程序同时发送数据包到同一个Socket连接上时,这些数据包有可能在接收端粘连在一起,形成一个数据包。
1.2 TCP 粘包问题的具体表现
TCP粘包问题的具体表现主要体现在接收端数据的处理上。
首先,接收端收到的数据可能包含了多个发送端发送的消息。这是因为 TCP 协议在传输数据时,可能将多个小数据包合并成一个大数据包发送,而接收端在读取数据时,通常是一次性读取缓冲区中的所有内容。因此,如果发送端连续发送多个小数据包,而接收端的缓冲区大小足够大,那么这些小数据包就有可能被合并成一个大数据包发送给接收端。接收端在读取这个大数据包时,由于无法准确区分原本的数据包边界,就可能将多个发送端的消息混在一起处理,导致数据解析错误。
其次,一个消息可能被分成多个数据包发送。这通常发生在发送端发送的消息长度超过了 TCP 发送缓冲区剩余空间大小或 MSS(最大段大小)时。在这种情况下,TCP 协议会在传输前对消息进行拆包处理,将其分成多个小数据包发送。然而,由于网络传输的不确定性,这些小数据包可能在接收端不是按照发送的顺序到达的,或者由于接收端缓冲区的大小限制,这些小数据包可能被拆分或合并后存入缓冲区。因此,接收端在读取数据时,可能会发现原本应该是一个完整消息的数据被拆分成了多个数据包,导致数据解析和处理的复杂性增加。
此外,TCP 粘包问题还可能导致接收端的数据处理出现混乱。由于粘包现象的发生,接收端在读取数据时可能无法准确判断当前读取的数据是完整的一个消息还是多个消息的拼接。这可能导致接收端在处理数据时发生错误,例如将原本属于不同消息的数据错误地解析为同一个消息的一部分,或者将原本应该是一个完整消息的数据错误地截断或丢弃。这种混乱的数据处理可能导致应用程序的逻辑错误、数据丢失或重复等问题。
综上所述,TCP 粘包问题的具体表现主要包括接收端收到的数据包含多个发送端的消息、一个消息被分成多个数据包发送以及接收端数据处理出现混乱等方面。为了避免和解决这些问题,需要在应用程序的设计和实现中采取适当的措施,如添加消息长度字段、使用定长报文或应用层协议等方式来明确区分数据包边界,以确保数据的正确解析和处理。
2 处理 TCP 粘包问题的策略
处理TCP粘包问题的策略主要包括以下几种:
-
设置消息边界:
- 添加特殊字符或标志符号:在消息的末尾添加特定的分隔符,如"\r\n"或者自定义的协议标识。接收方在读取数据时,会不断检查是否有这些分隔符,一旦找到就认为当前的消息已经完整,从而避免了粘包问题。例如,FTP协议就使用了这种方式。
- 使用消息帧:定义一种消息帧格式,每个消息都被封装在一个帧中,帧的头部包含消息长度等信息。接收方先读取帧头部获取消息长度,再按照该长度读取完整的消息内容。
-
定长发送:
- 发送固定长度的数据包:无论实际数据大小如何,都将其填充或截断为固定长度的数据包进行发送。接收方按照同样的固定长度来读取数据,从而避免了粘包问题。但这种方法可能会浪费带宽,特别是当实际数据较小时。
- 改进版定长发送:对于最后一个长度不足的数据包,可以在其后面填充空白字节,并在数据包头部添加一个长度字段或标志位,以便接收方能够识别并去除这些填充字节。
-
延迟发送:
- 发送方等待一段时间:在发送一个数据包后,发送方等待一段时间再发送下一个数据包,以减少多个数据包同时到达接收方造成的粘包问题。但这种方法可能增加数据传输的延迟,影响实时性。
-
优化接收方处理:
- 调整接收缓冲区大小:根据实际应用场景和数据量大小,合理设置接收方的缓冲区大小,避免缓冲区过小导致的粘包问题。
- 多线程或异步接收:使用多线程或异步处理机制来接收数据,以提高数据处理速度和效率,减少粘包问题的影响。
-
应用层协议设计:
- 设计明确的应用层协议:在应用层设计明确的协议规范,包括数据包格式、长度字段、消息边界等,以确保发送方和接收方能够正确解析和处理数据。
在实际应用中,可以根据具体场景和需求选择适合的策略来处理TCP粘包问题。同时,还需要考虑网络状况、数据传输量、实时性要求等因素,综合权衡各种策略的优缺点,以达到最佳的处理效果。
3 使用消息头+消息体法处理 TCP 粘包问题
3.1 基本概念
使用消息头(Header)+消息体(Body)的方法来处理 TCP 粘包问题是一种常见且有效的策略。这种方法的核心思想是在每个消息前添加一个消息头,消息头中包含了消息体的长度信息,接收方在读取数据时首先读取消息头,然后根据消息头中的长度信息来读取相应长度的消息体,从而避免了粘包问题。
具体实现步骤如下:
发送方:
- 将要发送的消息封装成消息体(Body)。
- 生成一个消息头(Header),消息头中至少包含消息体的长度信息。可以使用定长的整数来表示长度,也可以使用可变长度的编码方式(如使用前缀编码来表示长度)。
- 将消息头和消息体合并成一个完整的数据包。
- 将数据包通过 TCP 连接发送给接收方。
接收方:
- 创建一个缓冲区来接收数据。
- 不断从 TCP 连接中读取数据到缓冲区,直到缓冲区中有足够的数据来读取一个完整的消息头。
- 解析消息头,获取消息体的长度信息。
- 根据消息体的长度信息,从缓冲区中读取相应长度的数据作为消息体。
- 将消息头和消息体组合成一个完整的消息,并进行后续处理。
- 重复步骤 2-5,直到所有数据都被处理完毕。
这种方法的优点在于它能够清晰地界定每个消息的边界,从而避免了粘包问题。同时,它也能够处理变长的消息,提高了数据传输的灵活性。
需要注意的是,在实际应用中,还需要考虑一些边界情况和异常处理。例如,当接收方读取到的数据不足以构成一个完整的消息头时,应该继续等待数据的到来;当接收到的数据长度超过消息头中指定的长度时,应该丢弃多余的数据或进行相应的错误处理。此外,为了提高数据传输的效率,还可以在消息头中添加其他元数据信息,如时间戳、消息类型等,以便接收方更好地理解和处理消息。
3.2 示例
下面是一个简化的 C++ 代码示例,用于使用消息头(固定为0x20)+消息体长度(占一个字节)+消息体的格式来处理TCP粘包问题。
首先,定义粘包处理函数:
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <cstdint>
#include <algorithm> std::queue<uint8_t> remainingDatas; // 用于临时存储没有处理完的数据
bool revMsgHeadFlag = false; // 是否收到消息头
bool revMsgLenFlag = false; // 是否收到消息体长度
uint8_t messageLength = 0; // 消息体长度void clearRemainingDatas() {while (!remainingDatas.empty()) {remainingDatas.pop();}
}// 处理TCP粘包问题的函数
void processTCPData(const std::vector<uint8_t>& receivedData) {for (auto val : receivedData){remainingDatas.push(val);}while (!remainingDatas.empty()) {// 检查是否接收到消息头 if (!revMsgHeadFlag) {auto val = remainingDatas.front();remainingDatas.pop();if (0x20 != val) {std::cerr << "Invalid header found!" << std::endl;messageLength = 0;clearRemainingDatas();revMsgHeadFlag = false;revMsgLenFlag = false;return; // 可以进行其他的错误处理 }revMsgHeadFlag = true;}if (!revMsgLenFlag && !remainingDatas.empty()) {messageLength = remainingDatas.front();remainingDatas.pop();revMsgLenFlag = true;} if (!revMsgLenFlag) {// 数据不完整,等待更多数据 break;}// 检查是否有足够的数据来读取整个消息体 if (messageLength > remainingDatas.size()) {// 数据不完整,等待更多数据 break;}// 读取消息体 std::vector<uint8_t> messageBody;for (size_t i = 0; i < messageLength; i++){auto val = remainingDatas.front();remainingDatas.pop();messageBody.emplace_back(val);}// 处理消息体(这里只是简单打印) std::cout << "Received message: ";for (uint8_t byte : messageBody) {std::cout << static_cast<char>(byte);}std::cout << std::endl;// 准备读取下一个消息revMsgHeadFlag = false;revMsgLenFlag = false;messageLength = 0;}// 如果有剩余数据未处理,可能是因为数据不完整,需要等待更多数据 if (remainingDatas.size() > 0) {std::cout << "Remaining data, waiting for more..." << std::endl;}
}
接下来,使用模拟的字节流数据做测试:
int main() {// 模拟TCP接收数据 std::vector<uint8_t> receivedData1 = {0x20, 0x06, 'H', 'e', // 消息1: 消息头 + 长度3 + 消息体"He" : 注意,这里消息体少了三个字节,放在下一包数据};std::vector<uint8_t> receivedData2 = {'l','l','o',' ', 0x20, 0x06, 'W', 'o', 'r', 'l', 'd', '!',0x20, 0x06, 'H', 'e', 'l', 'l', 'o',' ',0x20, // 消息2: 上一包消息体的"llo " + 两个完整的消息 +下一包的消息头"0x20"};std::vector<uint8_t> receivedData3 = {0x04, 'T', 'C', 'P', '!' // 消息3: 长度4 + 内容"TCP!" };// 处理接收到的数据 processTCPData(receivedData1);processTCPData(receivedData2);processTCPData(receivedData3);return 0;
}
上面代码的输出为:
Remaining data, waiting for more...
Received message: Hello
Received message: World!
Received message: Hello
Received message: TCP!
在这个示例中,processTCPData 函数负责处理这些数据,它遍历接收到的字节流,查找消息头,读取消息体长度,并提取消息体。如果数据不完整,函数会停止处理并等待更多数据。
注意:这个示例非常简化,没有处理网络编程中的许多实际问题,比如多线程、异步I/O、错误处理、超时、流量控制等。在实际应用中,可能需要将这些概念整合到实际的网络编程框架中。此外,这个示例假设消息体的长度不会超过255字节(因为一个字节可以表示的最大值是255),如果需要处理更长的消息体,则需要调整消息体长度的编码方式。
相关文章:
突破编程_C++_网络编程(Windows 套接字(处理 TCP 粘包问题))
1 TCP 协议与粘包问题概述 1.1 TCP 粘包的产生原因 TCP粘包问题的产生原因涉及多个方面,主要的原因如下: 首先,发送方在发送数据时,由于TCP协议为提高传输效率而采用的Nagle算法,可能会将多个小数据包合并成一个大数…...

【训练营】DateWhale——动手学大模型应用开发(更新中)
文章目录 写在前面大模型简介LLM简介RAG简介LangChain开发框架开发LLM应用的整体流程 写在前面 大模型时代从GPT爆发开始到现在已有一年多了,深度学习发展之快无法想象,一味感叹技术发展速度超越个人学习速度是没用的,倒不如花点时间参加一些…...

【学习笔记十九】EWM Yard Management概述及后台配置
一、EWM Yard堆场管理业务概述 1.Yard Management基本概念 YARD管理针对的是库房以外的区域,可以理解为入大门开始到库门之前的这部分的区域 堆场结构 像在仓库中一样,将相应仓位映射为堆场仓位,可将其分组到堆场分区。场地中可能具有以下结…...

【环境搭建】(五)Ubuntu22.04安装cuda_11.8.0+cudnn_8.6.0
一个愿意伫立在巨人肩膀上的农民...... 设备配置: 一、安装GCC 安装cuda之前,首先应该安装GCC,安装cuda需要用到GCC,否则报错。可以先使用下方指令在终端查看是否已经安装GCC。 gcc --version 如果终端打印如下则说明已经安装…...

【UE5.1】使用MySQL and MariaDB Integration插件——(3)表格形式显示数据
在上一篇(【UE5.1】使用MySQL and MariaDB Integration插件——(2)查询)基础上继续实现以表格形式显示查询到的数据的功能 效果 步骤 1. 在“WBP_Query”中将多行文本框替换未网格面板控件,该控件可以用表格形式布局…...

JVM复习
冯诺依曼模型与计算机处理数据过程相关联: 冯诺依曼模型: 输入/输出设备存储器输出设备运算器控制器处理过程: 提取阶段:输入设备传入原始数据,存储到存储器解码阶段:由CPU的指令集架构ISA将数值解…...
63、ARM/STM32中IIC相关学习20240417
完成温湿度传感器数据采集实验。 【思路:1.通过IIC通信原理,理解其通信过程,通过调用封装的IIC函数达成主机和从机之间:起始信号、终止信号、读、写数据的操作; 2.了解温湿度传感器控制芯片SI7006的工作原理&#…...

离岸人民币与人民币国际化
参考 什么是离岸人民币?它有什么用? - 知乎 “人民币就是人民币,为什么要在它前面加上离岸二字?” “既然有离岸人民币,是否有在岸人民币?” 今天我们就简单了解一下什么是离岸人民币。 离岸/在岸人民币…...
Linux平台上部署和运行Ollama的全面指南
Ollama的安装与配置 Ollama提供了一种简单的安装方法,只需一行命令即可完成安装,但是对于想要更深入了解和自定义安装的用户,我们也提供了手动安装的步骤。 快速安装 Ollama的安装极为简单,只需在终端中执行以下命令࿱…...
Web---robots协议详解
在Web中,robots协议(也称为robots.txt)是一种文本文件,用于向搜索引擎机器人(通常称为爬虫)提供指导,以指示它们哪些页面可以抓取,哪些页面应该忽略。robots.txt文件位于网站的根目录…...

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第四套
华为海思校园招聘-芯片-数字 IC 方向 题目分享——第四套 (共9套,有答案和解析,答案非官方,仅供参考)(共九套,每套四十个选择题) 部分题目分享,完整版获取(WX:didadida…...
clipper一些数据结构(入门初识(一))
clipper一些数据结构(一) Clipper库是一个用于执行多边形裁剪(clipping)和偏移(offsetting)操作的开源C库。在Clipper库中,点和多边形(polygon)是基本的数据结构。Clipp…...

读《SQL基础教程 第二版 上》的一些总结
1. 数据库语言 DDL: Data Definition Language,数据定义语言(库、表的操作) DML: Data Manipulation Language, 数据操控语言(对表中数据的增删改) DQL: Data Query Language,数据库查询语言…...

EDI是什么:EDI系统功能介绍
EDI全称Electronic Data Interchange,中文名称是电子数据交换,也被称为“无纸化贸易”。EDI实现企业间(B2B)自动化通信,帮助贸易伙伴和组织完成更多的工作、加快物流时间并消除人为错误。 目前国内企业实现EDI通信大多…...

64B/66B GT Transceiver 配置
一、前言 前一篇文章已经讲述了64B/66B的编码原理,此篇文章来配置一下7系列GT的64B/66B编码。并讲述所对应的例子工程的架构,以及部分代码的含义。 二、IP核配置 1、打开7 Series FPGAs Transceiver Wizards,选择将共享逻辑放置在example …...

ES6: promise对象与回调地狱
ES6: promise对象与回调地狱 一、回调地狱二、Promise概述三、Promise的组成四、用函数封装Promise读取文件操作 一、回调地狱 在js中大量使用回调函数进行异步操作,而异步操作什么时候返回结果是不可控的,所以希望一段程序按我们制定的顺序执…...
Qt事件处理机制2-事件函数的传播
所有继承自QObject的类都有event函数,该函数用来处理自身的事件,函数定义如下: virtual bool QObject::event(QEvent *e);Qt帮助文档: This virtual function receives events to an object and should return true i…...

【PDF.js】PDF文件预览
【PDF.js】PDF文件预览 一、PDF.js二、PDF.js 下载1、下载PDF.js2、在项目中引入3、屏蔽跨域错误 三、项目中使用四、说明五、实现效果 使用PDFJS实现pdf文件的预览,支持预览指定页、关键词搜索、缩略图、页面尺寸调整等等。 一、PDF.js 官方地址 文档地址 二、PD…...

从建表语句带你学习doris_表索引
1、doris建表概述 1.1、doris建表模板 CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [DATABASE.]table_name (column_definition1[,column_deinition2,......][,index_definition1,[,index_definition2,]] ) [ENGINE [olap|mysql|broker|hive]] [key_desc] [COMMENT "tabl…...

Linux CentOS 安装 MySQL 服务教程
Linux CentOS 安装 MySQL 服务教程 1. 查看系统和GNU C库(glibc)版本信息 1.1 查询机器 glibc 版本信息 glibc,全名GNU C Library,是大多数Linux发行版中使用的C库,为系统和应用程序提供核心的API接口。在Linux系统中,特别是在…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...

VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...