Nagle算法原理与实现详解

news2024/12/25 15:18:23

文章目录

  • 背景
  • 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 main

import (
	"net"
	"sync"
	"time"
)

//这里使用互斥锁来保证线程安全
var mu sync.Mutex

func 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://www.coloradmin.cn/o/571952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lenovo IdeaPad 330-15IKB 81DE电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网,转载需注明出处。(下载请直接百度黑果魏叔) 硬件配置 硬件型号驱动情况 主板Lenovo IdeaPad 330-15IKB 81DE 处理器Intel(R) Core(TM) i3-8130U CPU 2.20GHz (Kaby Lake Refresh)已驱动 内存M471A1G44AB0-CWE * …

jetson nano csi摄像头 tensorrt 运行yolov8检测

jetson nano csi摄像头 tensorrt 运行yolov8检测 1. 在本地电脑训练环境下将onnx模型导出yolov8 导出onnx 模型使用onnxsim优化onnx 模型2. 在jetson nano下 转换到tensorrt模型配置好环境后 使用trtexec 生成engine使用python tensorrt 读取csi摄像头进行预测1. 在本地电脑训练…

AnsiConsole-能够编写 ANSI 转义序列的控制台

Spectre.Console 是一款 .NET 库,提供了一种简单但强大的方式来创建美观和交互式的控制台应用程序。它允许开发人员轻松构建具有颜色、表格、进度条等功能的富命令行界面 (CLI)。 功能 Spectre.Console 的一些显着功能包括: 颜色:Spectre.C…

实时时钟 RTC(2)

RTC 使能与停止 RTC 上电后立即启动,不可关闭,软件应在32K 晶体振荡器完全起振后再设置当前时间;在晶体振荡器起振之前芯片使用内部环振计时,偏差较大。 RTC 时间设置 软件可以在任意时刻直接设置RTC 时间寄存器;由于…

数据结构与算法(八)

二叉搜索树 二叉树:二叉树每个结点最多有两个子树 二叉搜索树: (BST) 其实就是在普通的二叉树上加了一些限制 没有任何限制,二叉搜索树插入子结点的时候有一些特殊的要求 二叉搜索树的性质: 非空左子树所有的键值小于其根结点的键值非空右子树所有的…

Java的Atomic原子类

Java SDK 并发包里提供了丰富的原子类,我们可以将其分为五个类别,这五个类别提供的方法基本上是相似的,并且每个类别都有若干原子类。 对基本数据类型的变量值进行原子更新;对对象变量的指向进行原子更新;对数组里面的…

如何使用 Fail2ban 防止对 Linux 的暴力攻击?

在当今数字化世界中,网络安全成为了一个极其重要的话题。Linux 作为一种广泛使用的操作系统,也面临着各种网络攻击的风险,包括暴力攻击、密码破解和恶意登录等。为了保护 Linux 系统的安全,我们可以使用 Fail2ban 这样的工具来防止…

什么是日志关联

什么是日志关联 日志关联是一种分析来自不同源的日志数据以识别事件模式的技术。它用于更好地了解网络的活动,从而有效地保护网络免受漏洞和威胁。 日志关联是日志管理过程的关键部分。收集和存储日志后,集中式日志服务器将执行分析以检测特定事件。日…

Java Web 编写第一个Servlet程序全过程

一、工具准备 IDEATomcat 二、创建一个名为hello-servlet的maven项目 三、在工程根目录下创建一个web文件夹&#xff0c;web文件夹下创建WEB-INF目录&#xff0c;WEB-INF目录下创建web.xml文件&#xff0c;目录结构以及web.xml文件内容如下&#xff1a; <?xml version&qu…

AcrelEMS-HIM高速公路综合能效系统在新晋高速公路快村营至营盘段项目的应用

​安科瑞 耿敏花 摘 要&#xff1a;我国新型工业化、信息化、城镇化和农业现代化加快发展&#xff0c;经济结构加快转型&#xff0c;交通运输总量将保持较快增长态势&#xff0c;各项事业发展要求提高国家公路网的服务能力和水平。高速公路沿线的收费站、互通枢纽、服务区、隧道…

三年外包的血泪史,终于脱离苦海了~

外包给的不高&#xff0c;是真的不能去呀&#xff01; 先说一下我的个人情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近3年的功能测试点点点&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒…

C# WPF窗体设计器显示以及App.xaml文件打不开(VS 2022)

问题描述&#xff1a; 在项目中遇到了App.xaml设计器打不开以及窗体设计器不显示&#xff0c;只有代码&#xff0c;如图所示&#xff1a; 可以明显的看见左下角的设计器不见&#xff0c;但是用户控件又有设计器 解决方法&#xff1a; (一、App.xaml不能正常打开) ①清理项…

数字孪生智慧工厂可视化分析决策方案,打造智慧汽车工厂

智慧工厂是当前智能制造领域的热门话题之一&#xff0c;是一种集成数字技术、先进制造技术和现代管理技术的新型工厂模式。随着全球制造业的发展&#xff0c;智慧工厂逐渐成为未来工厂发展的一大趋势&#xff0c;越来越多的企业开始关注智慧工厂的建设。 该数字孪生智慧汽车工厂…

vue-cli4+vant+rem+sass+vuex+axios封装+webpack搭建前端项目

移动端项目模板 基于 vue-cli4.0 webpack 4 vant ui sass rem 适配方案axios 封装&#xff0c;构建手机端模板脚手架 启动项目 git clone https://github.com/teach-tian/h5-vue-cli4.gitcd h5-vue-cli4npm installnpm run serve✅ 配置多环境变量 package.json 里的 s…

Stimulsoft报表开发工具支持电子签名啦!一起来看

Stimulsoft Reports 是一款报告编写器&#xff0c;主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署&#xff0c;如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等&#xff0c;在你的应用程序中嵌入报告设计器…

java 注解学习

Java 语言中存在三类注解&#xff0c;分别是元注解&#xff08;Meta-annotations&#xff09;、Java 内置注解&#xff08;Built-in Annotations&#xff09;和自定义注解&#xff08;Custom Annotations&#xff09;。 1、元注解&#xff08;Meta-annotations&#xff09; 元…

Pandas-如何对指定某列的NaN值进行替换或填充

前言 本文是该专栏的第31篇,后面会持续分享python数据分析的干货知识,记得关注。 笔者在本专栏之前有单独详细介绍过,使用Numpy对数组元素进行替换的方法,感兴趣的同学,可翻阅查看“Numpy-如何对数组的元素进行替换”。 而本文来单独介绍pandas对指定列的NaN值进行操作的…

《汇编语言》- 读书笔记 - 实验5 编写、调试具有多个段的程序

《汇编语言》- 读书笔记 - 实验5 编写、调试具有多个段的程序 题目1题目2题目3题目4题目5题目6总结 题目1 将下面的程序编译、连接&#xff0c;用 Debug 加载、跟踪&#xff0c;然后回答问题 assume cs:code, ds:data, ss:stack data segmentdw 0123h,0456h,0789h,0abch,0def…

github添加ssh-key来支持git项目管理

背景 https://github.com很多时候无法克隆/更新/提交项目&#xff0c;使用gitgithub.com怎没有限制 配置git账户邮箱和用户名 查看配置信息 git config --global --list 配置或者修改用户名&#xff0c;替换为自己github用户名 git config --global user.name "holyl…