什么是粘包?

article/2025/10/13 16:42:04

TCP/IP 协议簇建立了互联网中通信协议的概念模型,该协议簇中的两个主要协议就是 TCP 和 IP 协议。TCP/ IP 协议簇中的 TCP 协议能够保证数据段(Segment)的可靠性和顺序,有了可靠的传输层协议之后,应用层协议就可以直接使用 TCP 协议传输数据,不在需要关心数据段的丢失和重复问题。

tcp-and-application-protocols

图 1 - TCP 协议与应用层协议

IP 协议解决了数据包(Packet)的路由和传输,上层的 TCP 协议不再关注路由和寻址,那么 TCP 协议解决的是传输的可靠性和顺序问题,上层不需要关心数据能否传输到目标进程,只要写入 TCP 协议的缓冲区的数据,协议栈几乎都能保证数据的送达。

当应用层协议使用 TCP 协议传输数据时,TCP 协议可能会将应用层发送的数据分成多个包依次发送,而数据的接收方收到的数据段可能有多个『应用层数据包』组成,所以当应用层从 TCP 缓冲区中读取数据时发现粘连的数据包时,需要对收到的数据进行拆分。

所以, 什么是粘包?

粘包:多个数据包被连续存储于连续的缓存中,在对数据包进行读取时由于无法确定发生方的发送边界,而采用某一估测值大小来进行数据读出,若双方的size不一致时就会使指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

出现粘包的原因?

出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。

先说简单的接收方原因, 接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。

再说由发送导致的粘包, 这个比较有意思.

粘包并不是 TCP 协议造成的,它的出现是因为应用层协议设计者对 TCP 协议的错误理解,忽略了 TCP 协议的定义并且缺乏设计应用层协议的经验。我们将从 TCP 协议以及应用层协议出发,分析我们经常提到的 TCP 协议中的粘包是如何发生的:

  • TCP 协议是面向字节流的协议,它可能会组合或者拆分应用层协议的数据;
  • 应用层协议的没有定义消息的边界导致数据的接收方无法拼接数据;

TCP 协议是面向连接的、可靠的、基于字节流的传输层通信协议,应用层交给 TCP 协议的数据并不会以消息为单位向目的主机传输,这些数据在某些情况下会被组合成一个数据段发送给目标的主机。

Nagle 算法是一种通过减少数据包的方式提高 TCP 传输性能的算法。因为网络 带宽有限,它不会将小的数据块直接发送到目的主机,而是会在本地缓冲区中等待更多待发送的数据,这种批量发送数据的策略虽然会影响实时性和网络延迟,但是能够降低网络拥堵的可能性并减少额外开销。

在早期的互联网中,Telnet 是被广泛使用的应用程序,然而使用 Telnet 会产生大量只有 1 字节负载的有效数据,每个数据包都会有 40 字节的额外开销,带宽的利用率只有 ~2.44%,Nagle 算法就是在当时的这种场景下设计的。

当应用层协议通过 TCP 协议传输数据时,实际上待发送的数据先被写入了 TCP 协议的缓冲区,如果用户开启了 Nagle 算法,那么 TCP 协议可能不会立刻发送写入的数据,它会等待缓冲区中数据超过最大数据段(MSS)或者上一个数据段被 ACK 时才会发送缓冲区中的数据。

nagle-algorithm

图 2 - Nagle 算法

几十年前还会发生网络拥塞的问题,但是今天的网络带宽资源不再像过去那么紧张,在默认情况下,Linux 内核都会使用如下的方式默认关闭 Nagle 算法:

TCP_NODELAY = 1

Linux 内核中使用如下所示的 tcp_nagle_test 函数测试我们是否应该发送当前的 TCP 数据段,感兴趣的读者可以以这段代码为入口详细了解 Nagle 算法在今天的实现:

static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb,unsigned int cur_mss, int nonagle)
{if (nonagle & TCP_NAGLE_PUSH)return true;if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN))return true;if (!tcp_nagle_check(skb->len < cur_mss, tp, nonagle))return true;return false;
}

Nagle 算法确实能够在数据包较小时提高网络带宽的利用率并减少 TCP 和 IP 协议头带来的额外开销,但是使用该算法也可能会导致应用层协议多次写入的数据被合并或者拆分发送,当接收方从 TCP 协议栈中读取数据时会发现不相关的数据出现在了同一个数据段中,应用层协议可能没有办法对它们进行拆分和重组。

除了 Nagle 算法之外,TCP 协议栈中还有另一个用于延迟发送数据的选项 TCP_CORK,如果我们开启该选项,那么当发送的数据小于 MSS 时,TCP 协议就会延迟 200ms 发送该数据或者等待缓冲区中的数据超过 MSS。

无论是 TCP_NODELAY 还是 TCP_CORK,它们都会通过延迟发送数据来提高带宽的利用率,它们会对应用层协议写入的数据进行拆分和重组,而这些机制和配置能够出现的最重要原因是 — TCP 协议是基于字节流的协议,其本身没有数据包的概念,不会按照数据包发送数据。

如何解决粘包?

如果我们系统性地学习过 TCP 协议以及基于 TCP 的应用层协议设计,那么设计一个能够被 TCP 协议栈任意拆分和组装数据包的应用层协议就不会有什么问题。既然 TCP 协议是基于字节流的,这其实就意味着应用层协议要自己划分消息的边界。

如果我们能在应用层协议中定义消息的边界,那么无论 TCP 协议如何对应用层协议的数据包进程拆分和重组,接收方都能根据协议的规则恢复对应的消息。在应用层协议中,最常见的两种解决方案就是基于长度或者基于终结符(Delimiter)。

message-framing

图 3 - 实现消息边界的方法

基于长度的实现有两种方式,一种是使用固定长度,所有的应用层消息都使用统一的大小,另一种方式是使用不固定长度,但是需要在应用层协议的协议头中增加表示负载长度的字段,这样接收方才可以从字节流中分离出不同的消息,HTTP 协议的消息边界就是 基于长度+负载长度 实现的:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 138
...
Connection: close<html><head><title>An Example Page</title></head><body><p>Hello World, this is a very simple HTML document.</p></body>
</html>

在上述 HTTP 消息中,我们使用 Content-Length 头表示 HTTP 消息的负载大小,当应用层协议解析到足够的字节数后,就能从中分离出完整的 HTTP 消息,无论发送方如何处理对应的数据包,我们都可以遵循这一规则完成 HTTP 消息的重组。

虽然知道http-header中有Content-Length,以为只是一个简单的标记左右,现在才知道是为了解决粘包问题.

不过 HTTP 协议除了使用基于长度的方式实现边界,也会使用基于终结符的策略,当 HTTP 使用块传输(Chunked Transfer)机制时,HTTP 头中就不再包含 Content-Length 了,它会使用负载大小为 0 的 HTTP 消息作为终结符表示消息的边界。

还有在使用post进行表单上传文件时, 会有一个boundary字符串(大概张这样, --ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC), 这个也是作为文件的一个分隔符, 也可以说是基于终结符策略的.

当然除了这两种方式之外,我们可以基于特定的规则实现消息的边界,例如:使用 TCP 协议发送 JSON 数据,接收方可以根据接收到的数据是否能够被解析成合法的 JSON 判断消息是否终结。

总结

TCP 协议粘包问题是因为应用层协议开发者的错误设计导致的,他们忽略了 TCP 协议数据传输的核心机制 — 基于字节流,其本身不包含消息、数据包等概念,所有数据的传输都是流式的,需要应用层协议自己设计消息的边界,即消息帧(Message Framing),我们重新回顾一下粘包问题出现的核心原因:

  1. TCP 协议是基于字节流的传输层协议,其中不存在消息和数据包的概念;
  2. 应用层协议可以使用基于长度或者基于终结符的消息边界,解决多个消息的粘连;

http://chatgpt.dhexx.cn/article/ea3SRwxM.shtml

相关文章

【HUST】信息系统安全:Ret2libc多函数调用,ASLR两种情况(2)

注&#xff1a;感谢这位大佬的帮忙&#xff0c;没有他我估计还在github里面或者其他博客里面瞎找小雨aaa Ret2libc:Return to libc,顾名思义&#xff0c;就是通过劫持控制流使控制流指向libc中的系统函数&#xff0c;从而实现打开shell等其他工作。 在本次作业中&#xff0c;…

Linux ALSA音频工具

参考&#xff1a; ALSA 音频工具 amixer、aplay、arecord Linux Alsa ALSA的配置文件 音频录制——arecord 音频播放——aplay 音频配置——amixer alsamixer与amixer的区别 alsamixer是Linux音频框架ALSA工具之一&#xff0c;用于配置音频各个参数; alsamixer是基于文本图形…

[pwn]ROP:绕过ASLRNX

[详细] ROP&#xff1a;绕过ASLR&NX 这次使用的程序是Defcon - 2015初赛题目&#xff0c;r0pbaby&#xff0c;也是一道经典的pwn题目了。 程序链接&#xff1a;https://pan.baidu.com/s/1kr6z_crZfW7qNjtASmRMGw 提取码&#xff1a;eajs NX策略是指在栈中的代码不会被执行…

ORA-445报错与ASLR

数据库多次出现ORA-00445: background process "J002" did not start after 30 seconds报错及ORA-3136错误 查看相关文档(文档 ID 1600807.1)&#xff0c;两个报错都可能与内存压力过大有关 另外关于ORA-00445还有另一篇文档提到&#xff0c;在Oracle启用ASLR会无法…

[二进制学习笔记]Ubuntu20.04关闭开启ASLR

文章目录 Ubuntu20.04关闭开启ASLR Ubuntu20.04关闭开启ASLR ​ ASLR(Address space layout randomization)是一种针对缓冲区溢出的安全保护技术&#xff0c;通过对堆、栈、共享库映射等线性区布局的随机化&#xff0c;通过增加攻击者预测目的地址的难度&#xff0c;防止攻击者…

ASLR和PIE的区别

总结&#xff1a;ASLR 不负责代码段以及数据段的随机化工作&#xff0c;这项工作由 PIE 负责。但是只有在开启 ASLR 之后&#xff0c;PIE 才会生效。

【HUST】信息系统安全:Ret2libc多函数调用,ASLR两种情况(1)

Ret2libc:Return to libc,顾名思义&#xff0c;就是通过劫持控制流使控制流指向libc中的系统函数&#xff0c;从而实现打开shell等其他工作。 在本次作业中&#xff0c;我们的目标是通过运行stack.c程序来访问系统上的/tmp/flag程序的内容&#xff0c;其中&#xff0c;可以看到…

Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)

1. ASoC的由来 ASoC--ALSA System on Chip &#xff0c;是建立在标准ALSA驱动层上&#xff0c;为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系。在ASoc出现之前&#xff0c;内核对于SoC中的音频已经有部分的支持&#xff0c;不过会有一些局限性&#xff1a…

astrill android,Astrill

你想知道你所用的网络速度是多少吗&#xff1f;你想在全球任何地方都可以使用到手机网络吗&#xff1f;你想在需要下载文件时定位到信号最强的地方进行传输吗&#xff1f;小编今天为企业家和用户安利一款手机测速游戏——Astrill APP&#xff01;用户可以随时随地的查询到自己的…

Linux下 ASLR功能与 -no-pie 选项说明

一. Linux下ASLR功能 1. ASLR 技术介绍 ASLR 技术是一种针对缓冲区溢出的安全保护技术。 ASLR&#xff0c;全称为 Address Space Layout Randomzation&#xff0c;地址空间布局随机化。ASLR 技术在 2005 年的 kernel 2.6.12 中被引入到 Linux 系统&#xff0c;它将进程…

ASLR和PIE的区别和作用

ASLR和PIE的区别和作用 ASLR的作用 首先ASLR是归属于系统功能的&#xff0c; aslr是一种针对缓冲区溢出的安全保护技术&#xff0c;通过对堆、栈、共享库映射等线性区布局的随机化&#xff0c;通过增加攻击者预测目的地址的难度&#xff0c;防止攻击者直接定位攻击代码位置&…

Linux下关闭ASLR(地址空间随机化)的方法

##0x00 背景知识 ASLR(Address Space Layout Randomization)在2005年被引入到Linux的内核 kernel 2.6.12 中&#xff0c;当然早在2004年就以patch的形式被引入。随着内存地址的随机化&#xff0c;使得响应的应用变得随机。这意味着同一应用多次执行所使用内存空间完全不同&…

ASLR技术

简述 ASLR&#xff08;Address Space Layout Randmoization&#xff0c;地址空间布局随机化&#xff09;是一种针对于缓冲区溢出的安全保护技术。 windows 内核版本 OS内核版本windows 20005.0windows XP5.1windows Server 20035.2windows Vista6.0windows Server 20086.0wi…

ASLR

ASLR 一、ASLR是什么&#xff1f;二、测试ASLR技术1.一个简单的源文件2.生成ALSR.exe与ALSR_no.exe3.使用OllDbg调试器查看程序入口地址与栈地址3.1 ASLR.exe3.2 ASLR_no.exe 4.使用CFF Explorer查看PE文件信息4.1 重定位表的区别4.2 IMAGE_FILE_HEADER/Characteristics属性4.3…

[ASLR,地址空间,Linux,随机化,Windows]ASLR 是如何保护 Linux 系统免受缓冲区溢出攻击的

地址空间随机化&#xff08;ASLR&#xff09;是一种内存攻击缓解技术&#xff0c;可以用于 Linux 和 Windows 系统。了解一下如何运行它、启用/禁用它&#xff0c;以及它是如何工作的。 -- Sandra Henry-stocker 地址空间随机化(Address Space Layout Randomization)&#xff0…

ALSR

一 ALSR介绍&#xff1a; 1.1定义 aslr是一种针对缓冲区溢出的安全保护技术&#xff0c;通过对堆、栈、共享库映射等线性区布局的随机化&#xff0c;通过增加攻击者预测目的地址的难度&#xff0c;防止攻击者直接定位攻击代码位置&#xff0c;达到阻止溢出攻击的目的的一种技…

如何提高强化学习算法模型的泛化能力?

深度强化学习实验室 官网&#xff1a;http://www.neurondance.com/ 来源&#xff1a;https://zhuanlan.zhihu.com/p/328287119 作者&#xff1a;网易伏羲实验室 编辑&#xff1a;DeepRL 在深度学习中&#xff0c;模型很容易过拟合到参与训练的数据集。因此&#xff0c;深度学习…

BatchFormer:有效提升数据稀缺场景的模型泛化能力|CVPR2022

文 | 侯志知乎&#xff08;已授权&#xff09;源 | 极市平台 摘要 当前的深度神经网络尽管已经取得了巨大的成功&#xff0c;但仍然面临着来自于数据稀缺的各种挑战&#xff0c;比如数据不平衡&#xff0c;零样本分布&#xff0c;域适应等等。 当前已经有各种方法通过样本之间的…

图解深度学习-提高泛化能力的方法

数据集 在深度学习的训练过程中&#xff0c;神经网络的类型和结构固然重要&#xff0c;但训练样本才是重中之重。 数据增强 当训练样本数量较少时&#xff0c;可以通过数据增强来增加样本的多样性。 数据增强就是通过对样本图像进行平移、旋转、镜像翻转等方式进行变换。除…

JAVASE提高 -- Java泛型

1. 泛型类 &#xff08;1&#xff09;使用语法 类名<具体的数据类型> 对象名 new 类名<具体的数据类型>(); &#xff08;2&#xff09;Java1.7以后&#xff0c;后面的<>中的具体的数据类型可以省略不写 类名<具体的数据类型> 对象名 new 类名<&…