Nagle算法原理与实现详解

article/2025/7/23 11:15:31

文章目录

  • 背景
  • Nagle算法详解
    • 算法实现
    • 实现
    • 开启与关闭Nagle算法
  • Nagle算法与延迟ACK
  • 参考

背景

TCP的数据流大致可以被分成两类:

  • 交互式数据流

    TCP交互数据流指的是:TCP连接中传输的所有数据的总和,包括控制命令(用于管理网络中连接,传输数据和处理错误,如telnet的sendput命令)和应用程序的数据

  • 成块数据流

    成块数据流是用来发送数据的包,网络上大部分都是这种包。TCP中,数据被分成了一个一个的TCP数据包进行传输,成块数据包指的是,在上层应用视角,TCP提供了一个连续无间断的数据流。

TCP在传输这两种数据包时的效率不同。为了提高TCP效率,需要对这两种包采用不同的算法。其中的原则是:尽量减少小分组传输的数量

Nagle算法详解

在TCP连接中,任意时刻只能有一个未被确认的小片段。在发送出去的报文中,必须要等待对方发送ACK之后,服务端才会发送一个新的报文。

Nagle算法的主要目的是为了预防小分组的产生,因为在广域网中,小分组会造成网络拥塞

当网络中存在大量小分组时,网络拥塞出现的可能性会增加,因为每个小分组都需要占据网络带宽路由器缓存空间。由于TCP要求每个小分组发送之前都需要进行确认,分组数量过多会导致消息数量增多,从而导致确认消息的数量增加,进而导致网络延迟吞吐量下降。

Nagle算法的原理:

Nagle要求一个TCP连接上最多只能有一个未被确认的小分组。这意味着,在发送完一个小分组后,需要一直等待该分组的确认ACK到达,否则不会发送其他的分组。当确认到达之后,TCP会收集已经准备好的小分组,并将它们合并成一个大的分组发送出去,从而减少了网络拥塞的可能性,降低了网络延迟,并提高了吞吐量

缺点

  • 严重影响请求响应式协议的延迟(例如Redis中是把Nagle算法禁止掉的,避免延迟,因为Redis的命令都要求尽快的传输到Redis服务器,而不需要等待其他命令或缓冲区填充)
  • 对于实时性要求很高的交互上,我们也禁止使用Nagle

算法实现

Nagle算法的实现是通过下面的伪代码表述的:

if 有数据要发送:
{if 可用窗口大小>=MSS and 可发送数据>=MSS:立即发送MSS大小的数据else:if 有未确认的数据:将数据放入缓存等待ack
}
else:立即发送数据

具体来说,Nagle算法的实现原理和过程是这样的:

  • 接收到了客户端发送过来的ACK。
  • 如果包长度达到了MSS(1460),则允许发送。
  • 如果该数据包含有FIN,则允许发送。
  • 如果设置了TCP_NODELAY,则允许发送。
  • 如果达到了超时时长(200ms),则允许发送。
  • 如果未设置TCP_CORK,且所有已发出去的小数据包的个数超过了最大值,默认是200个,则允许发送。

该算法的精妙之处在于实现了一个自时钟控制。ACK返回得越快,数据传输就越快,这使得在单位时间内发送的报文更少。

实现

//Go 模拟实现Nagle算法
/*实现Nagle算法的核心就是在发送数据的时候进行缓存和延迟*/
package mainimport ("net""sync""time"
)//这里使用互斥锁来保证线程安全
var mu sync.Mutexfunc main() {//conn变量代表与服务段的TCP连接,这个是一个客户端conn, err := net.Dial("tcp", "localhost:8080")if err != nil {panic(err)}//用于在函数执行完之后,自动断开连接defer conn.Close()//模拟Nagle算法:缓存小的数据包,在一定事件或者缓存到一定程度的时候发送//sendbuffer只哦叛逆用来缓冲需要发送的小数据包,等待合并sendBuffer := make([]byte, 0) //创建一个空的字节片,长度为0,可以将空的字节片看作一个缓冲区,在需要的时候进行动态扩容maxSendBufferSize := 1024     //需要缓存的最大容量delayedSecond := time.Second  //发送的延迟时间,发送小数据包等待的最大时间lastSendTime := time.Now()    //当前时间//向服务器发送数据msg1 := "hello"msg2 := "world"//将数据缓存到,//将byte数据一个字节一个字节添加到sendBuffer中sendBuffer = append(sendBuffer, []byte(msg1)...)sendBuffer = append(sendBuffer, []byte(msg2)...)for {mu.Lock()if len(sendBuffer) > 0 {idleSecond := time.Since(lastSendTime) //计算距离上一次有多少时间//如果当前缓存的数据量达到了指定的发送大小或者时间已经达到了超时时间。就需要发送if len(sendBuffer) >= maxSendBufferSize || (idleSecond >= delayedSecond && len(sendBuffer) > 0) {n, err := conn.Write(sendBuffer)if err != nil || n == 0 {panic(err)}lastSendTime = time.Now()//因为已经成功发送了n个字节,所以就需要从n位置开始截取新的切片,进行下一次的发送sendBuffer = sendBuffer[n:] //这个截取一个切片}}mu.Unlock()}
}

开启与关闭Nagle算法

当chOPt=1时关闭Nagle算法,不用等待ACK可以连续的发送
当chOpt=0时打开Nagle算法

int   nErr=setsockopt(   m_socket,   IPPROTO_TCP,   TCP_NODELAY,   &chOpt,   sizeof(char));  

Nagle算法与延迟ACK

延迟ACK指接收端等待延时ACK计时器后统一对接收到的报文进行ACK,而非每个报文都立即ACK。

Nagle算法指规定一段时间内,只有一个报文会在传输,等待缓冲区满或者收到ACK才会发送新报文。

当客户端(client)发送消息给服务器(server)时,若客户端收到ACK后等待延时ACK计时器结束才进行延迟应答,服务器由于未收到对方的ACK而一直等待,可导致死锁,此时只有等待延时计时器结束(至少40ms)才能解决死锁问题。

write-write-read模式对服务器而言也会造成类似的延迟问题,例如在需要发送两个报文 A、B的情况下,使用此模式时发送 A 报文后,服务器会等待客户端发送 A 报文的ACK,但如果客户端延迟响应,导致过了一段时间才发送ACK,才会发送 B 报文。这会极大增加延迟。

针对此问题,有两种解决方案:

  1. 将两个 write 合并,变成一个 write-read 的过程,从而避免使用Nagle算法导致的延迟问题。
  2. 禁用Nagle算法。

参考

深入浅出TCPIP之Nagle算法

【TCP/IP】Nagle 算法以及所谓 TCP 粘包

TCP之Nagle算法和延迟确认及关闭参数

【网络编程实践】2.3.4.2 建议关闭 Nagle 算法

【1】TCP/IP 详解 卷1:协议


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

相关文章

TCP详解 (三)Nagle算法和延迟确认

文章目录 延迟确认Nagle算法Nagle算法遇上延迟确认关闭Nagle算法 一些有关TCP通信量的研究如[Caceresetal.1991]发现,如果按照分组数量计算,约有一半的TCP报文段包含成块数据(如 FTP、电子邮件和 Usenet新闻),另一半则…

复指数信号正交性的简单证明

复指数信号为: . 写成矢量形式: 复指数信号两两正交,也就是两个复指数信号的内积有如下表示: 将上式内积形式展开: 当kl时,所有cos项1,sin项0,求和为N。 当时,将求和符…

三角函数正交性的推导

定理: 一个三角函数系:1 cosx sinx cos 一个三角函数系:1,cosx , sinx , cos2x , sin2x , … , cosnx , sinnx , … 如果这一堆函数(包括常数1)中任何两个不同函数的乘积在区间[-π, π]上的积分等于…

OFDM时频脉冲形状与子载波正交性的理解

从多载波调制的角度来理解子载波间正交,首先要明确OFDM默认的脉冲成型是时域矩形窗,时域上每个子载波 e j 2 π f n t e^{j2πf_nt} ej2πfn​t 都被一个矩形窗脉冲 g T ( t ) g_T(t) gT​(t)成型。其次,OFDM子载波间隔 等于 OFDM符号周期的…

正交性原理与维纳霍夫(正则)方程

有期望信号d(n),纯净信号x(n),以及噪声信号g(n); 有滤波器h(m),以及滤波器输出信号y(n),滤波器输出纯净信号x的估计值y&a…

三大变换与自控(五)三角函数的正交性证明

在本系列文章的第一篇中,我们提到任何信号都可以被分解为三角函数,是因为三角函数的正交性,因此在三角函数构建的坐标系中可以绘制任何图形。现在我们就来证明一下三角函数的正交性,以完善我们整个推导过程。 教材上的三角函数系…

机器学习的数学基础(3):正交性原理(orthogonality principle)

思考这样一个问题,令S为一个希尔伯特空间,而空间S的一个子空间 当我们给定了,如何求最近上距离x最近的点。 则我们用数学语言表示该问题为一个优化问题: 该问题的解可以直接通过一个定理给出,即正交性定理&#xff0…

傅里叶级数与傅里叶变换_Part1_三角函数系的正交性

傅里叶级数与傅里叶变换_Part1_三角函数系的正交性 参考链接: DR_CAN老师的原视频 0、复习Part0的内容 参考链接:傅里叶级数与傅里叶变换_Part0_欧拉公式证明三角函数和差公式证明 三角函数的和差公式如下 sin ⁡ ( α β ) sin ⁡ ( α ) cos ⁡ …

09 正交性

09 正交性 9-1 空间,向量空间和欧几里得空间 9-2 广义向量空间 9-3 子空间 9-4 维度 9-5 行空间和矩阵的行秩 9-6 列空间 9-7 矩阵的秩和矩阵的逆 9-8 零空间与看待零空间的三个视角 9-9 零空间与秩-零化度定理 9-10 左零空间,四大子空间和研究子空间的…

CSS正交性分析

Refer: 为什么 CSS 这么难学? 我先来解释一下什么是正交。你调过显示器的「亮度」、「色调」和「饱和度」吧。 「亮度」就是明暗程度,值越大,屏幕越亮。 「色调」就是颜色,你通过调色调,可以把红色调成绿色。 「饱和度…

勒让德多项式的正交性和归一化

罗德里格斯公式正交性归一化应用 这学期上数学课时老师布置了一道习题:计算勒让德多项式的模。翻看本科数学物理方法教材,发现计算方法较复杂,且用到了生成函数 为了方便理清整个计算过程,这一博客直接从罗德里格斯公式出发并避免…

向量组的正交性

向量的内积定义 运算: 向量的正交性: 正交向量组的性质: 向量组的正交规范化 正交矩阵 定义: 正交矩阵的判定

拉盖尔多项式的正交性

标准拉盖尔多项式 拉盖尔多项式可以表示为: 拉盖尔多项式的正交性是指 当 时 上式的积分运算结果为0。这是一种加权的正交性。 证明: (1) 采用变换 容易得到,当 上式的结果为0是因为在进行微分运算后,各项均包含 , 各项的上下限均为0。 …

三角函数系的正交性

参考资料: https://zhuanlan.zhihu.com/p/341796771https://www.bilibili.com/video/BV1Et411R78v

1 三角函数的正交性

三角函数的正交性 三角函数的正交性三角函数系证明 三角函数的正交性 三角函数系 集合 { s i n 0 x , c o s 0 x , s i n x , c o s x , s i n 2 x , c o s 2 x , . . . } \lbrace sin0x, cos0x, sinx,cosx,sin2x,cos2x,... \rbrace {sin0x,cos0x,sinx,cosx,sin2x,cos2x,...…

正交的概念

“正交性”是从几何学中借来的术语。如果两条直线相交成直角,它们就是正交的,比如图中的坐标轴。用向量术语说,这两条直线互不依赖。沿着某一条直线移动,你投影到另一条直线上的位置不变。 在计算技术中,该术语用于表示…

正交性,从內积开始到施密特正交化

正交性 前言內积、长度和正交性[1]內积长度和距离正交向量非正交向量 正交集,正交基和正交投影正交集基定理1 正交基定理2 正交投影非零向量投影直线上的投影空间投影正交分解定理 格拉姆-施密特正交化参考 前言 多维空间,向量和矩阵,以及正…

三角函数正交性理解与Matlab分析

1.什么是正交性? “正交性”是从几何中借来的术语。如果两条直线相交成直角,他们就是正交的。在空间向量中,两个向量的标量积为零即两个向量正交。 如果两个函数满足,则称这两个函数正交。 2.什么是三角函数正交信号集&#xf…

mysql初期密码修改方式

1、适用解压版本的mysql 2、初始化mysql后,会随机生成一个密码,但比较复杂,(#一定记住) 初始化命令 mysqld -initialize 3、初始化后必须修改初始密码才能对mysql进行操作,这个时候操作就会报错误消息。 …