在使用一些协议通讯的时候,比如Telnet,会有一个字节字节的发送的情景,每次发送一个字节的有用数据,就会产生41个字节长的分组,20个字节的IP Header 和 20个字节的TCP Header,这就导致了1个字节的有用信息要浪费掉40个字节的头部信息,这是一笔巨大的字节开销,而且这种Small packet在广域网上会增加拥塞的出现。

如果解决这种问题? Nagle就提出了一种通过减少需要通过网络发送包的数量来提高TCP/IP传输的效率,这就是Nagle算法

Nagle算法

Nagle算法主要是避免发送小的数据包,要求TCP连接上最多只能有一个未被确认的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些少量的小分组,并在确认到来时以一个分组的方式发出去。

  1. if there is new data to send
  2. if the window size >= MSS and available data is >= MSS
  3. send complete MSS segment now
  4. else
  5. if there is unconfirmed data still in the pipe
  6. enqueue data in the buffer until an acknowledge is received
  7. else
  8. send data immediately
  9. end if
  10. end if
  11. end if

从上述算法中看出:

1. 对于MSS的片段直接发送

2. 如果有没有被确认的data在缓冲区内,先将待发送的数据放到buffer中直到被发送的数据被确认【最多只能有一个未被确认的小分组

3. 两种情况置位,就直接发送数据,实际上如果小包,但是没有未被确认的分组,就直接发送数据。

这里通过一个实验来看下Nagle算法对于发送的优化:

实验要求:Client端每次发送1个字节,将hello发送到Server端,然后server再全部发送给Client,其实要点在于Client的发送,预期的结果是:

1. 我们虽然一个字节一个字节的发,但是在协议中使用Nagle算法,可能会有延时等待的状况,即将几个字符合成一个片段进行发送

2. 必须是收到对方的确认之后,才能再次发送

看下实验的结果:

从图中的结果可以看出

1. HELLO 被分成 2个包发送了,应用层调用send 5次,由于Nagle算法,将ELLO合成一个包发送,这样大可以减少Samll packet的数量,增加TCP传输的效率

2. 分成的2个数据包,并没有连续被发出,这也符合Nagle算法的原则,即TCP连接上最多只能有一个未被确认的小分组,等待收到ACK之后,才发第二个封包。

禁用Nagle算法

在默认的情况下,Nagle算法是默认开启的,Nagle算法比较适用于发送方发送大批量的小数据,并且接收方作出及时回应的场合,这样可以降低包的传输个数。同时协议也要求提供一个方法给上层来禁止掉Nagle算法

当你的应用不是连续请求+应答的模型的时候,而是需要实时的单项的发送数据并及时获取响应,这种case就明显不太适合Nagle算法,明显有delay的。

linux提供了TCP_NODELAY的选项来禁用Nagle算法。

禁用方法:

  1. setsockopt(client_fd, SOL_TCP, TCP_NODELAY,(int[]){1}, sizeof(int));

来看下禁用后同样发送Hello的实验结果

从实验结果中可以得出如下结论:

1. 禁止Nagle算法,每一次send,都会组一个包进行发送,HELLO被分成5个小包分别发送

2.不用等待ACK,可以连续发送

Delay ACK and Nagle

Nagle指出Nagle算法与Delay ACK机制有共存的情况下会有一些非常糟糕的状况,比如举一个场景:PC1和PC2进行通信,PC1发数据给PC2,PC1使用Nagle算法,PC2有delay ACK机制

1. PC1发送一个数据包给PC2,PC2会先不回应,delay ACK

2. PC1再次调用send函数发送小于MSS的数据,这些数据会被保存到Buffer中,等待ACK,才能再次被发送

从上面的描述看,显然已经死锁了,PC1在等待ACK,PC2在delay ACK,那么解锁的代价就是Delay ACK的Timer到期,至少40ms[40ms~500ms不等],也就是2种算法在通信的时候,会产生不必要的延时!

可以看下实验的图示, 9包是发送H字符到server,可以看到隔了30ms的delay ack延时才等到数据,发送一个DATA+ACK包,在这个时间段内其实也是用包发送的,但是nagle算法是要等待ACK的到来才能发包的,所以也会看到11号包要在ACK包之后。如果30ms延时仍然没有数据,就是我们上述说的那样白白的等待一个delay ack超时。

如何来解决这种问题?

其实Nagle算法本身的立意是好的,避免网络充斥着过多的小包,提高网络传输的效率,同时Delay ACK也是为了提高TCP的性能,不过二者遇到了,就比较悲剧了。其实在RFC中已经提供了一个用户级别的解决方案,即避免 write--write--read的这种写法,write--read--write--read 以及write--write--write都是OK的。假设这里有数据要发送,这个数据分为头部和数据部分,2部分发送,然后再回读响应,写法如下

1.write(header)

2.write(body)

3.read(response)

服务器read写法如下

1. read(request)

2. process(request)

3. write(response)

应用程序使能了Nagle算法,第一个header是一定能够发送出去的,因为前面没有带确认的数据,服务器端接收到header之后,也发现是不完全的,还会再次等待request,同时要delay ACK,再次发write的时候,发现没有ACK,也会等待ACK延迟发送。这样只能超时才能再次传输。

这个问题的产生,主要是Nagle和delay ACK 副作用以及write write read的程式造成的。一般写程序的时候不推荐这样的写法。如何解决? 可能想到的就是上面的禁止掉Nagle算法,这也是一种办法,不过这种办法同时也会让网络充斥小包,降低效率。对于应用程序来讲,只要避免write-wirte-read的这种写法就可以避免掉问题,比如write一块去写,一次写成功,就一个包发过去了,就没有等待delay的过程了!所以写程序的时候还是要注意再注意。

TCP-IP详解:Nagle算法的更多相关文章

  1. TCP/IP详解 (转)

    TCP/IP详解学习笔记(1)-基本概念 为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中 ...

  2. TCP IP详解(转)

    大学学习网络基础的时候老师讲过,网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 网络七层协议简称OSI.TCP/IP刨除了物理层,并把上三层(会话层.表示层和应用层)统称 ...

  3. 《TCP/IP详解卷1:协议》第19章 TCP的交互数据流-读书笔记

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

  4. TCP/IP详解学习笔记 这位仁兄写得太好了

      TCP/IP详解学习笔记(1)-基本概念 为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣 ...

  5. TCP/IP详解学习笔记 这位仁兄写得太好了.(转载)

    TCP/IP详解学习笔记   这位仁兄写得太好了   TCP/IP详解学习笔记   这位仁兄写得太好了. http://blog.csdn.net/goodboy1881/category/20444 ...

  6. 【转】TCP/IP详解学习笔记(二)

    TCP/IP详解学习笔记(5)-IP选路,动态选路,和一些细节 1.静态IP选路 1.1.一个简单的路由表 选路是IP层最重要的一个功能之一.前面的部分已经简单的讲过路由器是通过何种规则来根据IP数据 ...

  7. 《TCP/IP详解:卷一》-TCP部分讲解

    TCP/IP协议 作者:Danbo 2015-7-2 本文为参考TCP/IP详解卷一,某些知识点加上了作者自己的理解,如有错误,欢迎指正,可以微博联系我! TCP包格式和IP包格式如下: TCP的正常 ...

  8. 《TCP/IP详解卷1:协议》第3章 IP:网际协议(1)-读书笔记

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

  9. 《TCP/IP详解卷1:协议》第6章 ICMP:Internet控制报文协议-读书笔记

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

  10. 《TCP/IP详解卷1:协议》第11章 UDP:用户数据报协议-读书笔记

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

随机推荐

  1. Maven的scope的值

    Maven的依赖范围 在pom.xml文件中,有个元素是scope,用来表示依赖的范围.之所以会有依赖范围,是因为Maven在编译.测试和运行项目时会各自使用一套classpath,依赖范围就是用来控 ...

  2. jQuery 是javascript的一个库(常用插件、处理器)

    jQuery校验官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation jQuery就是javascript的一个库,把我 ...

  3. 开启你的kali linux DIY之旅

    更新源 首先 是kali2016.2更新源的问题,网上找了好久,都不是很满意.后来把kali 2016.2安装到实体机中才发现,里面自带了更新源.下面我把这个官方自带更新源分享出来(速度蛮快的) de ...

  4. 2018跳槽面试必备之深入理解 Java 多线程核心知识

    导语:多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线 ...

  5. linux查看内存free

    free 加参数-b/k//m/g,以b.k.m.g的大小显示结果,默认以k显示 [root@oldboy ~]# free total used free shared buffers cached ...

  6. 设置控件如ImageButton可见与否

    继承view的控件有三种ui属性: 1.setVisibility(View.Gone); 不可见,不占有空间 2.setVisibility(View.VISIBLE); 可见 3.setVisib ...

  7. IntelliJ IDEA 连接数据库 详细过程

    IntelliJ IDEA集成了众多插件,方便开发者使用,使用其自带的Database模块就可以很方便的配置.连接到数据库,本次操作以MySQL为例,其中testjdbc数据库已经提前建好,里面有两张 ...

  8. Easyui 官网网址

    http://www.jeasyui.com/download/list.php 下载版本1.5.2的easyui中文API,可在CSDN网站http://download.csdn.net/down ...

  9. JavaScript 装饰者模式(this运用)

    例: function ConcreteClass() { this.performTask = function () { this.preTask(); console.log('doing so ...

  10. <OFFER> 06_PrintListInReversedOrder

    #include<stack> #include "List.h" void PrintListReversingly_Iteratively(ListNode* pH ...