当前位置: 首页 > news >正文

说一下 Tcp 粘包是怎么产生的?

TCP 粘包是什么?

TCP 粘包(TCP Packet Merging) 是指多个小的数据包在 TCP 传输过程中被合并在一起,接收方读取时无法正确分辨数据边界,导致数据解析错误。

TCP 是流式协议,没有数据包的概念,它只是保证数据按照字节流的顺序传输,不保证接收方能按照原始发送时的数据边界来接收数据。因此,TCP 可能会把多个数据包合并(粘包)或者拆分(拆包)


1. TCP 粘包的两种情况

(1)发送端导致的粘包

发送方的数据量较小,TCP 不会立即发送,而是等缓冲区满了再一起发送,这样可以减少网络开销。导致多个小数据包合并成一个大的数据包,产生粘包

示例

假设我们在 TCP 连接中连续发送三条消息:

send(socket, "Hello", 5, 0);
send(socket, "World", 5, 0);
send(socket, "!!!", 3, 0);

如果 TCP 将这三次 send 的数据合并在一起,接收方可能会收到:

HelloWorld!!!

这样就无法判断消息边界,导致解析困难。

原因

  • TCP 有 Nagle 算法(默认开启):
    • 小数据会被合并,等待缓冲区满了才一起发送,减少小包,提高传输效率。
    • 适用于高并发场景,但会导致粘包问题。
    • 可以通过 setsockopt 关闭:
      int flag = 1;
      setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
      

(2)接收端导致的粘包

接收方 读取数据不及时或者一次性读取了多个数据包,导致多个包的数据合并读取,形成粘包。

示例

如果发送方连续发送:

send(socket, "Hello", 5, 0);
send(socket, "World", 5, 0);
send(socket, "!!!", 3, 0);

接收方可能这样读取:

char buffer[20];
recv(socket, buffer, 20, 0);

如果 recv() 读取到了所有数据,buffer 里存的是:

HelloWorld!!!

但接收方可能预期每条消息是独立的,所以会出现粘包问题。

原因

  • TCP 是 流式传输,没有边界概念,recv() 读取数据时,可能一次读取多个包的内容
  • 如果接收方缓冲区没满,但程序没有及时读取,新的数据到来后会追加到原有数据里,造成粘包。

2. TCP 拆包(包被拆分)

除了粘包,拆包(packet fragmentation) 也是常见问题。
如果单次发送的数据超过了 TCP 最大传输单元(MTU),TCP 会自动拆分数据包

示例

假设 send() 发送 5000 字节,而 TCP 的 MTU 设为 1500 字节,则会拆分成:

Packet 1: 1500 bytes
Packet 2: 1500 bytes
Packet 3: 1500 bytes
Packet 4:  500 bytes

这样接收方 recv() 时可能会一次只收到部分数据,需要多次 recv() 才能完整还原。


3. 如何解决 TCP 粘包/拆包问题?

由于 TCP 没有消息边界,需要在应用层手动处理数据边界:

(1)固定长度协议

如果每条消息长度固定,可以按照固定字节数读取:

recv(socket, buffer, 10, 0);  // 一次读取 10 字节

但这种方法仅适用于所有消息长度一致的情况


(2)特殊分隔符

在消息结尾添加特殊字符,接收方按照这个字符分割数据:

send(socket, "Hello|", 6, 0);
send(socket, "World|", 6, 0);

接收方:

char buffer[1024];
recv(socket, buffer, 1024, 0);

然后通过 |拆分数据

char *token = strtok(buffer, "|");
while (token) {printf("Received message: %s\n", token);token = strtok(NULL, "|");
}

缺点

  • 需要保证 | 不会出现在正常数据中
  • 需要解析和处理数据,稍微增加了协议复杂度

(3)消息头 + 消息体(推荐)

在数据前面加上消息长度,接收方先读取长度,再读取完整数据:

struct Message {uint32_t length;  // 4字节,表示消息长度char data[1024];  // 消息体
};

发送数据:

uint32_t len = htonl(strlen(data));  // 转换为网络字节序
send(socket, &len, 4, 0);  // 先发送长度
send(socket, data, strlen(data), 0);  // 再发送数据

接收方:

uint32_t len;
recv(socket, &len, 4, 0);  // 先读取 4 字节长度
len = ntohl(len);  // 转换回主机字节序
recv(socket, buffer, len, 0);  // 再读取数据

优势

  • 适用于任何长度的消息,比定长方案更灵活。
  • 不会出现边界问题,比分隔符方案更可靠。

4. 总结

粘包的原因

  1. 发送端合并小数据包(TCP 缓冲区满了才发,Nagle 算法)。
  2. 接收端一次性读取多个数据包(TCP 没有消息边界)。

如何解决

方案适用场景复杂度
固定长度消息适用于消息长度固定的协议
特殊分隔符(如 \n、``)适用于文本协议(如 HTTP)
消息头 + 消息体(推荐)适用于二进制协议(如 TCP 长连接)

重点

  • TCP 是流式协议,没有边界,需要应用层协议解决粘包问题!
  • 消息头 + 消息体方式最通用,适用于大部分场景。🚀

这样就能高效避免 TCP 粘包问题啦!🎯

相关文章:

说一下 Tcp 粘包是怎么产生的?

TCP 粘包是什么? TCP 粘包(TCP Packet Merging) 是指多个小的数据包在 TCP 传输过程中被合并在一起,接收方读取时无法正确分辨数据边界,导致数据解析错误。 TCP 是流式协议,没有数据包的概念,…...

基于STM32设计的仓库环境监测与预警系统

目录 项目开发背景设计实现的功能项目硬件模块组成设计思路系统功能总结使用的模块的技术详情介绍总结 1. 项目开发背景 随着工业化和现代化的进程,尤其是在制造业、食品业、医药业等行业,仓库环境的监控和管理成为了至关重要的一环。尤其是在存储易腐…...

在uniapp中修改打包路径

在uniapp中修改打包路径,主要涉及到对manifest.json文件的编辑。以下是详细的步骤: 1. 确定当前uniapp项目的打包配置位置 uniapp项目的打包配置通常位于项目的根目录下的manifest.json文件中。这个文件包含了项目的全局配置信息,包括应用的…...

Kali Linux 渗透测试环境配置(Metasploit + Burp Suite)

一、Kali Linux 系统准备 首先,确保你已经成功安装了 Kali Linux 系统。可以从官方网站下载镜像文件,并通过 U 盘引导安装等常规方式完成系统部署。建议使用最新稳定版本,以获取最新的软件包支持和安全更新。 安装完成后,登录系…...

Oracle 变更redo log文件位置

更改Oracle数据库的Redo log文件位置,可以按照以下步骤操作。 1.查询当前Redo log文件信息 select * from v$log; select * from v$logfile;通过查询结果可知Redo log文件放在/oradata/redofile 目录下。 2.拷贝redo log文件到新的位置/Data/redolog $cd /orada…...

力扣题库第495题目解析

文章目录 1.题目再现2.思路分析&&示例说明2.1第一个示例2.2第二个示例 3.代码解释 1.题目再现 这个题目的名字叫做提莫攻击,如果是玩游戏的小伙伴对于这个场景就很熟悉了; 这个实际上是说:已知的条件会给我们一个数组,在…...

Milvus 存储设计揭秘:从数据写入到 Segment 管理的全链路解析

作为一款云原生向量数据库,Milvus 的高效查询性能有赖于其独特的存储架构设计。然而,在实际使用过程中,许多社区用户常常会遇到以下问题: 为什么频繁调用 flush 后,查询速度会变慢? 数据删除后,…...

单片机通讯中的时序图:初学者的入门指南

一、什么是时序图? 在单片机的世界里,时序图是一种非常重要的工具,它用于描述信号在时间上的变化规律。简单来说,时序图就像是信号的“时间线”,它展示了各个信号线在不同时间点上的电平状态。通过时序图,我…...

ASP.NET Core JWT

目录 Session的缺点 JWT(Json Web Token) 优点: 登录流程 JWT的基本使用 生成JWT 解码JWT 用JwtSecurityTokenHandler对JWT解码 注意 Session的缺点 对于分布式集群环境,Session数据保存在服务器内存中就不合适了&#…...

Linux基础命令之Nginx中的rewrite功能(重新)

一、什么是Rewrite Rewrite也称URL Rewrite,即URL重写,就是把传入Web的请求重定向到其他URL的过程。 1. URL Rewrite最常见的应用是URL伪静态化,是将动态页面显示为静态页面方式的一种技术。比如http://www.123.com/news/index.php?id123 使…...

4 前端前置技术(上):AJAX技术、Axios技术(前端发送请求)

文章目录 前言一、Ajax技术(从服务端获取数据,发送各种请求)0 接口文档管理:使用apipost等接口测试软件创建接口便于前端后端分离测试1 基本概念2 原生Ajax使用示例(几年前的早期用法) 二、 Axios技术(对原…...

三星手机为何不大力扩展中国市场?

三星在中国市场的手机销量长期低迷,主要原因可以归结为以下几点,这也解释了为什么三星可能没有大力扩展中国市场的计划: 1. 市场竞争激烈 中国市场已经被华为、OPPO、vivo、小米和苹果等品牌牢牢占据,这些品牌在产品设计、本地化…...

Linux在x86环境下制作ARM镜像包

在x86环境下制作ARM镜像包(如qemu.docker),可以通过QEMU和Docker的结合来实现。以下是详细的步骤: 安装QEMU-user-static QEMU-user-static是一个静态编译的QEMU二进制文件,用于在非目标架构上运行目标架构的二进制文…...

【算法篇】贪心算法

目录 贪心算法 贪心算法实际应用 一,零钱找回问题 二,活动选择问题 三,分数背包问题 将数组和减半的最小操作次数 最大数 贪心算法 贪心算法,是一种在每一步选择中都采取当前状态下的最优策略,期望得到全局最优…...

硬件电路基础

目录 1. 电学基础 1.1 原子 1.2 电压 1.3 电流 1.电流方向: 正极->负极,正电荷定向移动方向为电流方向,与电子定向移动方向相反。 2.电荷(这里表示负电荷)运动方向: 与电流方向相反 1.4 测电压的时候 2. 地线…...

iOS 音频录制、播放与格式转换

iOS 音频录制、播放与格式转换:基于 AVFoundation 和 FFmpegKit 的实现 在 iOS 开发中,音频处理是一个非常常见的需求,比如录音、播放音频、音频格式转换等。本文将详细解读一段基于 AVFoundation 和 FFmpegKit 的代码,展示如何实现音频录制、播放以及 PCM 和 AAC 格式之间…...

探索前端框架的未来:Svelte 的崛起

引言 在前端开发的世界里,框架更新换代的速度仿佛光速。从 jQuery 到 Angular,再到如今大热的 React 和 Vue,开发者们不断追逐更轻量、更快、更易于维护的框架。如今,Svelte 正悄然崛起,并引发了关于前端框架未来的热烈…...

Gitea+Gridea 创建个人博客

历史文档存档,该方法目前已经无法使用,部署方法可供参考 Gitea部分 1.关于Gitea Gitea 是一个面向开源及私有软件项目的托管平台,是全球最大的代码托管平台之一。它采用 Git 分布式版本控制系统,为开发者提供了代码托管、版本控…...

DeepSeek-V3:开源多模态大模型的突破与未来

目录 引言 一、DeepSeek-V3 的概述 1.1 什么是 DeepSeek-V3? 1.2 DeepSeek-V3 的定位 二、DeepSeek-V3 的核心特性 2.1 多模态能力 2.2 开源与可扩展性 2.3 高性能与高效训练 2.4 多语言支持 2.5 安全与伦理 三、DeepSeek-V3 的技术架构 3.1 模型架构 3…...

通过制作docker镜像的方式在阿里云部署前端后台服务

前端Dockerfile文件的内容: FROM nginx:版本,如果不指定,默认是latest COPY dist/ /usr/share/nginx/html/dist COPY nginx.conf /etc/nginx/nginx.conf EXPOSE 端口 前端sh脚本文件内容: appName项目名 tar -xvf dist.tar …...

无界构建微前端?NO!NO!NO!多系统融合思路!

文章目录 微前端理解1、微前端概念2、微前端特性3、微前端方案a、iframeb、qiankun --> 使用比较复杂 --> 自己写对vite的插件c、micro-app --> 京东开发 --> 对vite支持更拉跨d、EMP 方案--> 必须使用 webpack5 --> 很多人感觉不是微前端 --> 去中心化方…...

Linux(CentOS)安装 Nginx

CentOS版本:CentOS 7 Nginx版本:1.24.0 两种安装方式: 一、通过 yum 安装,最简单,一键安装,全程无忧。 二、通过编译源码包安装,需具备配置相关操作。 最后附:设置 Nginx 服务开…...

【提示词工程】探索大语言模型的参数设置:优化提示词交互的技巧

在与大语言模型(Large Language Model, LLM)进行交互时,提示词的设计和参数设置直接影响生成内容的质量和效果。无论是通过 API 调用还是直接使用模型,掌握模型的参数配置方法都至关重要。本文将为您详细解析常见的参数设置及其应用场景,帮助您更高效地利用大语言模型。 …...

GNN多任务预测模型实现(二):将EXCEL数据转换为图数据

目录 一. 引言 二. 加载和检查数据 三. 提取特征和标签 四. 标准化特征 五. 构建节点索引 六. 构建边及其特征 七. 总结 八. 结语 一. 引言 在图神经网络(Graph Neural Networks, GNNs)的多任务学习场景中,数据预处理是至关重要的一…...

.net的一些知识点6

1.写个Lazy<T>的单例模式 public class SingleInstance{private static readonly Lazy<SingleInstance> instance new Lazy<SingleInstance>(() > new SingleInstance());private SingleInstance(){}public static SingleInstance Instace > instance…...

Java 线程池:7参数配置、4拒绝策略与执行流程详解

1. 为什么需要线程池&#xff1f; 在 Java 并发编程中&#xff0c;线程的创建和销毁是一项昂贵的操作。频繁地创建和销毁线程会带来较高的系统开销&#xff0c;甚至可能因线程数过多而导致 OOM&#xff08;OutOfMemoryError&#xff09; 或 CPU 过载。 线程池&#xff08;Thre…...

SQL带外注入

SQL 带外注入&#xff08;Out-of-Band SQL Injection, OOB SQLi&#xff09; 是 SQL 注入的一种特殊类型&#xff0c;主要用于以下情况&#xff1a; 数据库没有直接返回错误信息&#xff08;比如被防火墙拦截了&#xff09;。无法使用常规注入手法&#xff08;如 UNION、错误信…...

MySQL知识点总结(一)

1.SQL分类 数据定义&#xff08;DDL&#xff09;:创/改/删/名/清&#xff08;cadrt&#xff09; 数据库对象&#xff1a;表/视图/存储/函数/触发器/事件 数据操作&#xff08;DML&#xff09;&#xff1a;增/删/改/查&#xff08;idus&#xff09; 操作数据库对象 数据控制&…...

【报错解决】MySQL报错:sql_mode=only_full_group_by

文章目录 报错信息 DataGrip 报错还原Navicat 报错还原 报错原因解决方案 查看当前 sql mode方案一&#xff1a;临时解决方案二&#xff1a;永久解决方案三&#xff1a;使用 any_value() 或 group_concat()方案四&#xff1a;调整实现思路&#xff0c;避开 GROUP BY 使用 我…...

【华为OD机考】华为OD笔试真题解析(1)--AI处理器组合

一、题目描述 某公司研发了一款高性能AI处理器&#xff0c;每台物理设备具备8颗AI处理器&#xff0c;编号分别为0、1、2、3、4、5、6、7。 编号0~3的处理器处于同一链路中&#xff0c;编号4~7的处理器处于另外一个链路中&#xff0c;不同链路中的处理器不能通信&#xff0c;如…...

【redis】缓存设计规范

本文是 Redis 键值设计的 14 个核心规范与最佳实践&#xff0c;按重要程度分层说明&#xff1a; 一、通用数据类型选择 这里我们先给出常规的选择路径图。 以下是对每个步骤的分析&#xff1a; 是否需要排序&#xff1f;&#xff1a; zset&#xff08;有序集合&#xff09;用…...

Node.js 实现简单爬虫

介绍 爬虫是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。 本文将使用 Nodejs 编写一个简单的爬虫脚本&#xff0c;爬取一个美食网站&#xff0c;获取菜品的标题和图片链接&#xff0c;并以表格的形式输出。 准备工作 1、初始化项目 首先&#xff0…...

一次奇怪的空指针问题分析:事务、死锁与隐式回滚

最近我们在排查一个诡异的 空指针异常&#xff0c;整个分析过程可以说是跌宕起伏&#xff0c;最终的结论也颇具隐蔽性。今天就把这个问题分享出来&#xff0c;希望对大家有所帮助。 问题现象 在系统中&#xff0c;我们有 单据 B&#xff0c;它通过一个 关联 ID 字段与 上级单…...

数据结构-基础

1、概念&#xff1a; 程序 数据结构 算法 2、程序的好坏 可读性&#xff0c;稳定性&#xff0c;扩展性&#xff0c;时间复杂度&#xff0c;空间复杂度。 3、数据结构 是指存储、组织数据的方式&#xff0c;以便高效地进行访问和修改。通过选择适当的数据结构&#xff0c; 能…...

问题大集04-浏览器阻止从 本地 发起的跨域请求,因为服务器的响应头 Access-Control-Allow-Origin 设置为通配符 *

1、问题 localhost/:1 Access to XMLHttpRequest at xxx&#xff08;请求&#xff09; from origin http://localhost:xxx&#xff08;本地&#xff09; has been blocked by CORS policy: The value of the Access-Control-Allow-Origin header in the response must not be t…...

音频进阶学习十二——Z变换一(Z变换、收敛域、性质与定理)

文章目录 前言一、Z变换1.Z变换的作用2.Z变换公式3.Z的状态表示1&#xff09; r 1 r1 r12&#xff09; 0 < r < 1 0<r<1 0<r<13&#xff09; r > 1 r>1 r>1 4.关于Z的解释 二、收敛域1.收敛域的定义2.收敛域的表示方式3.ROC的分析1&#xff09;当 …...

leetcode——组合总和(回溯算法详细讲解)

在面试或刷题过程中&#xff0c;回溯算法是一个绕不开的核心算法之一。今天&#xff0c;我们来详细解析 LeetCode 39「组合总和」 问题&#xff0c;并用 Java 回溯 剪枝优化 来高效解决它&#xff01;这篇文章不仅适合初学者&#xff0c;也适合希望提高回溯算法的朋友们。 给你…...

说一下JVM管理的常见参数

Java虚拟机&#xff08;JVM&#xff09;有许多常见参数&#xff0c;用于控制其行为和性能。以下是一些常见的JVM参数及其说明&#xff1a; 1. 内存管理参数 -Xms<size> START 设置初始堆内存大小。例如&#xff0c;-Xms512m表示初始堆大小为512MB。 -Xmx<size>…...

leetcode_47全排列II

1. 题意 给一个含有重复数字的数组&#xff0c;求不重复的排列。 2. 题解 将数组进行排序&#xff0c;当回溯发生的时候&#xff0c;找到下个不重复的元素即可。 class Solution { public:void genPerm(std::vector<std::vector<int>> &ans, std::vector&l…...

pytest-xdist 进行多进程并发测试!

在软件开发过程中&#xff0c;测试是确保代码质量和可靠性的关键步骤。随着项目规模的扩大和复杂性的增加&#xff0c;测试用例的执行效率变得尤为重要。为了加速测试过程&#xff0c;特别是对于一些可以并行执行的测试用 例&#xff0c;pytest-xdist 提供了一种强大的工具&…...

【CPP】CPP经典面试题

文章目录 引言1. C 基础1.1 C 中的 const 关键字1.2 C 中的 static 关键字 2. 内存管理2.1 C 中的 new 和 delete2.2 内存泄漏 3. 面向对象编程3.1 继承和多态3.2 多重继承 4. 模板和泛型编程4.1 函数模板4.2 类模板 5. STL 和标准库5.1 容器5.2 迭代器 6. 高级特性6.1 移动语义…...

STM32的HAL库开发---通用定时器(TIMER)---定时器脉冲计数

一、脉冲计数实验原理 1、 外部时钟模式1&#xff1a;核心为蓝色部分的时基单元&#xff0c;时基单元的时钟源可以来自四种&#xff0c;分别是内部时钟PCLK、外部时钟模式1&#xff0c;外部时钟模式2、内部定时器触发&#xff08;级联&#xff09;。而脉冲计数就是使用外部时钟…...

在C#中,Array,List,ArrayList,Dictionary,Hashtable,SortList,Stack的区别

Array Array你可以理解为是所有数组的大哥 普通数组 : 特点是长度固定, 只能存储相同类型的数据 static void Main(string[] args){//声明int[] ints;string[] strings;People[] peoples;//默认值 //int 类型是 0//string 类型是 nullint[] ints1 { 1, 2, 3 };string[] …...

Ollama 部署本地大语言模型

一、下载安装ollama 1.百度 ollama Ollama 2.点击下载 可以复制下载链接&#xff0c;使用下载器下载。 3.双击安装 默认安装目录&#xff1a;C:\Users\用户名\AppData\Local\Programs\Ollama 二、更改模型下载目录 0.默认下载目录 (跳过) 之前没下载过模型&#xff0c;不…...

sql批量更新和删除语句

1.更新一条数据 update om_sellorder set SOSTATUS2 where id 283d3eb87b134e1c993b70b018406285 2.更新多个数据为某一个特点值 string ID context.Request["ID"]; //需要替换‘,’逗号&#xff0c;不然识别不出ID数据这里注意 ‘ID’ 是一个逗号&#xff08;&a…...

探索从传统检索增强生成(RAG)到缓存增强生成(CAG)的转变

在人工智能快速发展的当下&#xff0c;大型语言模型&#xff08;LLMs&#xff09;已成为众多应用的核心技术。检索增强生成&#xff08;RAG&#xff09;&#xff08;RAG 系统从 POC 到生产应用&#xff1a;全面解析与实践指南&#xff09;和缓存增强生成&#xff08;CAG&#x…...

基于ArcGIS的SWAT模型+CENTURY模型模拟流域生态系统水-碳-氮耦合过程研究

流域是一个相对独立的自然地理单元&#xff0c;它是以水系为纽带&#xff0c;将系统内各自然地理要素连结成一个不可分割的整体。碳和氮是陆地生态系统中最重要的两种化学元素&#xff0c;而在流域系统内&#xff0c;水-碳-氮是相互联动、不可分割的耦合体。随着流域内人类活动…...

深入浅出:机器学习的全面解析

深入浅出&#xff1a;机器学习的全面解析 引言 机器学习&#xff08;Machine Learning, ML&#xff09;作为人工智能的一个重要分支&#xff0c;近年来取得了显著进展&#xff0c;并在多个领域中得到了广泛应用。本文将从基础概念、核心算法、应用场景以及未来发展趋势等方面…...

go运算符

内置运算符 算术运算符关系运算符逻辑运算符位运算符赋值运算符 算术运算符 注意&#xff1a; &#xff08;自增&#xff09;和–&#xff08;自减&#xff09;在 Go 语言中是单独的语句&#xff0c;并不是运算符 package mainimport "fmt"func main() {fmt.Printl…...

w196Spring Boot高校教师科研管理系统设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…...