热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

TCP流量控制与拥塞控制理解

目录流量控制滑动窗口滑动窗口为0的情况糊涂窗口综合征发送端解决“糊涂窗口”——Nagle算法接收端解决“糊涂窗口”——伪零窗口响应拥塞控制慢启动与拥塞避免慢启动

 

目录

流量控制

滑动窗口

滑动窗口为0的情况

糊涂窗口综合征

发送端解决“糊涂窗口”——Nagle算法

接收端解决“糊涂窗口”——伪零窗口响应

拥塞控制

慢启动与拥塞避免

慢启动

拥塞避免

快速重传与快恢复

快速重传

快恢复

总结




流量控制


滑动窗口

        滑动窗口主要用于进行流量控制,举个例子,A向B不停地发送数据,如果A发送数据速度比B接收数据速度快,那么最终就会出现B的接收缓存满的情况,此时对于A发送并溢出的数据,B是无法做出ACK应答的,而A收不到ACK应答就会继续重发,这样一来二去A就会发很多没有必要的报文。引起这一问题的原因,主要就是A并不知道B能接收多少数据,从而发送了很多B无法接收到的数据,而滑动窗口则解决了这一问题。

        滑动窗口是通过TCP报文中的“窗口大小”win字段实现的,对于B来说,“窗口大小”反映了B下一次还能接收多少数据(字节为单位),如此,B在回发给A的ACK报文中,携带的确认序号指定了B下一次期望接收的数据包序号,同时该ACK报文中也包含了B的窗口大小。A在收到B的ACK报文应答后,可以知道B的窗口大小以及下一次应该发的报文序号,根据这两条信息就可以确定A的发送窗口(如果只考虑流量控制的话,那么发送窗口大小就等于接收窗口大小),而发送窗口内的数据就是下一次应该发送的数据。


滑动窗口为0的情况

       如果A仍然一直发送数据,并且发送得很快,而B读取数据很慢,最终就会造成B窗口大小为0的情况。一旦B的窗口为0,那么B回发回来的ACK报文中就会显示win=0,此时A接收到了这条ACK报文,就知道B不能再接收数据,就不再给B发送数据了。

        而这种情况往往不会持续很久,当B从缓存区中读取了一些数据后,B的数据接收缓存中又有了一些存储空间,也就表示B又可以再接收数据了,但是此时A不知道啊!那该怎么去告诉A可以向B发送数据了呢?一种方法是在B的接收缓存有空间时,B直接向A发送ACK报文告诉A可以发送了,需要注意的是,A接收到这条ACK报文后是不需要发送确认的,B只管发出这条报文就坐等A再发数据过来,如果这条报文丢失,A也就不知道此时B可以接收数据,也一直在等待B,双方互相等待,最终就会造成死锁。

        为了解决这一问题,TCP为每一个连接都设有一个持续计时器(persistant timer)。


       当A收到B的零窗口通知(win=0),A就会启动这个持续计时器。当持续计时器超时,A就会向B发送一个探测报文段(仅携带一个字节数据)(这条报文并非是确认报文,如果收不到ACK确认会重传),而B收到这个探测报文段,就会再回发一个ACK报文,这条ACK报文中给出了B当前的窗口大小。A收到了这条报文后,如果发现窗口大小仍然为0,那么A就会重置持续计时器重新开始计时;如果窗口大小不为0,那么就会恢复正常状态向B发送数据。



糊涂窗口综合征

        通过滑动窗口,的确可以实现A与B端到端的数据传输流量控制,但是也会有其他情况:当B的数据缓存满了之后,如果B的数据处理速度足够慢,一次只能处理少许字节如1个字节,那么此时窗口大小就为1,A在探测到窗口大小为1后,就会向B发送一个字节的数据包,而一个数据包上只发送一个字节,可见是非常浪费的,如果B继续只处理一个字节,那么A就会一个字节一个数据包发出来,这样的话,在网络上就会有很多A的“小包”,这就是“糊涂窗口综合征”,增大了网络传输的负担,降低了网络效率。


发送端解决“糊涂窗口”——Nagle算法

       为了解决这一问题,从发送方来说,最直接的办法就是避免发送“小包”,这就是Nagle算法的作用。

       Nagle算法的核心就是:如果发送缓存区的是“小包”那就延迟发送,如果是“大包”那就直接发送。

       在发送缓存中,当数据一个字节一个字节地进来,发送方就会先把第一个数据字节发出去,把后面的数据缓存起来。当发送方收到了接收方对于第一个数据字节的确认后,就会把发送缓存中的数据组成一个报文段发送出去,并且对之后到达的数据继续缓存。只有等到收到前一条报文的确认后才继续发送下一条报文。这种等待前一条报文确认的过程,实际上就是一种延迟,通过延迟来不断积攒“小包”,直到收到确认,就表示延迟结束,就发出这些积攒的“小包”。当然,在确认还未发回时,可能“小包”就已经积攒成了“大包”,此时就无需再等待确认发回,直接发出“大包”即可。因此,Nagle算法还规定,如果发送缓存中的数据已经达到了发送窗口大小的一半,或者已经达到了MSS大小,那么就认定其为“大包”,直接发送即可。


总结来说,Nagle算法会在以下两种情况下发送:

1.发送缓存区中没有未被确认的数据;

2.发送缓存区中的数据长度达到发送窗口大小的一半或者大于一个MSS。

否则继续缓存数据。



接收端解决“糊涂窗口”——伪零窗口响应

        Nagle算法是从发送方的角度出发来解决“糊涂窗口综合征”,而从接收方角度出发也有相应办法解决,其解决思路是:

        当B的窗口大小降为0后,即使接收缓存区中的有数据被处理,如果空闲出来的窗口大小仍然只能容纳“小包”,那么接收方反馈给发送方探测报文的还是“接收窗口大小为0”。换句话说,只有当接收方的窗口大小能够容纳“大包”,接收方才会允许发送方发送数据给它。


这里的所说的“能够容纳‘大包’的窗口大小”是指满足以下两种情况之一:

1.接收缓存区足以容纳一个MSS大小的报文;

2.接收缓存区有一半的空闲空间。


        以上两种方法可以配合使用来解决“糊涂窗口综合征”。不过这两种方法虽然在广域网这种网络流量大的场合有不错的效果,但是在局域网这种流量小的网络中就可能显得得不偿失了。因为它们始终是通过延迟来避免“小包”传输的,而在网络流量小的场合,即使是传输“小包”影响也不会有多大,这种延迟是没有必要的,还可能导致不能及时响应请求的情况发生。




拥塞控制

        流量控制的对象往往是点对点通信量的控制,通过接收端来控制发送端的数据发送速率,从而使得接收端来得及接收。而拥塞控制则是针对于整个网络,用来防止过多的数据注入到网络中的,主要关注整个网络的输入负载是否是这个网络所能承受的。

        通过流量控制,可以实现对发送窗口的控制,根据接收方的接收能力来限制发送方的数据发送速率,而现在还要考虑网络的拥塞状况。为了考虑拥塞状况,发送方会维护一个叫做拥塞窗口cwnd的状态变量,这个变量会限制发送方在一个传输轮次中所能发送的最多报文数量。这里所提到的“一个传输轮次”,是指发送方将所有允许发送的报文全部发送后,到收到最后一个发送报文的确认的整个过程。

       那么,这个拥塞窗口与前面提到的发送窗口有什么关系呢?实际上,前面流量控制中提到的发送窗口是考虑接收方的接收能力,其大小也就等于接收窗口,而这里的拥塞窗口是考虑网络状况的,因此,发送方在实际发送数据时,最终会取二者中较小值作为发送窗口的大小。因此,拥塞控制实际上就是控制这个拥塞窗口的大小,控制方法有以下4种(不考虑流量控制):


慢启动与拥塞避免


慢启动

慢启动算法原理

       慢启动的算法原理是:一开始先将cwnd大小设置为1(1个MSS大小),即允许发送一个MSS大小的数据,此时发出1个报文段M1。当M1确认后,cwnd加1成为2,允许发送2个MSS大小的数据,此时就连续发出2个报文段M2、M3。当M2、M3确认后,cwnd加2为4,允许发送4个MSS大小的数据,此时就连续发送4个报文段M4~M7……如下所示:

       简单来说,通过慢启动算法,每经过一个传输轮次,当发送方收到最后一个报文段的确认后,cwnd的大小就加倍,由此使得大量需要连续发送的报文段“慢慢发送”而不是全部突然注入网络,从而避免网络拥塞现象。

慢启动算法存在的问题

        慢启动算法使得cwnd以指数型增长,没经过一次传输轮次就加倍一次,而cwnd一旦增大到某个值,就相当于允许连续发送很多报文段,那么拥塞控制的效果也就没有了。也就是当出现网络拥塞后,一旦一次发送的报文数超过了某个值,那么发出的报文就容易出现超时未确认的情况,而指数型增长的cwnd是很难控制的。


       举个例子,cwnd加倍到一次可以连续发送16个报文段,当这16个报文段全部返回确认后,cwnd再次加倍为32,此时暂且还没有出现拥塞的情况。到了下一轮次,就该连续发送32个报文段了,如果在发送第17个报文段的时候就出现了网络拥塞,那么17后面的报文段就都可能超时而收不到确认,发送方就会选择重发,这样就只会造成网络堵塞更加严重。


        为了避免这一问题,比较好的方法就是减缓cwnd的增长速度,而这也就是拥塞避免算法的作用。


拥塞避免

        拥塞避免算法的思路就是减缓拥塞窗口cwnd的增长速度,它的实现方式是每经过一个往返时间RTT就让cwnd加1(加上1个MSS的大小),这样就避免cwnd在轮次结束时直接加倍引起指数型增长,通过拥塞避免算法,使得cwnd每次只加1,成为缓慢的线性增长,在这种增长模式下,当发送方判断出现了网络拥塞后可以立即做出反应。

慢启动与拥塞避免结合

       那么,既然要减缓拥塞窗口增长速度,那直接让cwnd每次自增1就可以了,为什么还要用指数增长的慢启动呢?

       这是因为一开始cwnd本身就比较小,在较小的范围内,相比缓慢的线性增长,让cwnd增长更快一些也可以提高效率,而cwnd越来越大,当增长到某个值的时候就应当缓慢增长,以此避免网络拥塞。因此,慢启动算法与拥塞避免算法应当配合来使用,当cwnd较小时采用慢启动算法,而当cwnd较大时采用拥塞避免算法。

       因此,还应当考虑二者之间的界限,即何时由慢启动转为拥塞避免,这就需要设置一个慢开始门限ssthresh状态变量:


当cwnd

当cwnd>ssthresh时,停止使用慢启动算法开始使用拥塞避免算法;

当cwnd=ssthresh时,既可以使用慢启动算法,也可以使用拥塞避免算法。


慢启动与拥塞避免举例

       如上图所示,设置ssthresh的初始值为16。在前面的几次传输轮次中采用慢启动算法,此时cwnd以指数规律增长。

       第4个传输轮次结束后cwnd加倍到了16,达到慢启动门限值,此时采用拥塞避免算法,cwnd开始线性增大。

       到了第12个传输轮次,cwnd=24。报文发出后,发送方突然发现有报文超时未确认,尽管引起超时的情况有多种,但是此时发送方只会认为是网络拥塞引起的,也就是说此时的网络状况非常不好,需要“缓解一下”。因此直接将cwnd降到1,通过少发数据来缓解网络拥塞。除此之外,发送方还会减小ssthresh值,将ssthresh减少为发生拥塞时cwnd值的一半(如上图中cwnd=24时发生网络拥塞,于是就将ssthresh减小为24的一半为12),这就相当于通过提前降低cwnd增长速度来缓解网络拥塞。


快速重传与快恢复

       快速重传+快恢复算法是在慢启动+拥塞避免算法上进行补充与优化的算法。       


快速重传

       在慢启动+拥塞避免算法下,发送方认为发生“网络拥塞”的根据是因为发出的数据包超时未确认,也就是说,如果有个数据包丢了,那么就必须等到超时后下一个传输轮次再进行重传;而快速重传则是指:它要求接收方每收到一条失序报文段就立即确认,如果发送方收到了三次同一个报文的冗余ACK,此时发送方就会认为该报文段丢失了,那么不必等到超时计时器超时就立刻进行重传。通过快速重传可以使得网络的吞吐量提升20%。如下所示:


快恢复

       对于采用快速重传的发送方来说,当收到多次冗余的ACK后,它虽然会认为该报文段丢失了,但是它也会认为“很有可能没有发生网络阻塞”,因为如果发生了网络阻塞,那么也不可能收到多次冗余的ACK了。

       而在慢启动+拥塞避免算法下,在报文发生了超时未确认后,发送方就会认为此时肯定发生了网络拥塞,因此为了缓解网络拥塞,就将拥塞窗口cwnd大小会减为1,然后执行慢启动。

       由于是“很有可能没有发生网络阻塞”,因此对于采用快速重传的发送方来说,就没必要将拥塞窗口cwnd降到1,这样会做无谓的效率牺牲;但是为了降低网络拥塞的可能,也不能不降低cwnd。因此,在这种情况下,发送方就选择降低一部分cwnd,那么具体降低多少呢?这就是快恢复算法所做的事了:


在发生3次冗余ACK后,快恢复算法主要按序完成以下几件事:

1.慢启动门限值ssthresh设置为发生拥塞时cwnd的一半;

2.拥塞窗口大小cwnd设置为ssthresh+3;(这里依然以一个MSS的长度为单位,之所以还要再加上3,是因为发送方既然收到了3次冗余,那就说明前面发送的多个报文段中至少有3个报文段已经成功接收了,因此这一次可以多发送3个报文段)

3.如果再收到相同的冗余ACK,拥塞窗口大小就再加1;如果收到的是新报文的ACK,那么就复原慢启动门限值ssthresh,然后执行第4步;(即ssthresh再次回到发生快恢复前的状态)

4.执行拥塞避免算法。


慢启动+拥塞避免+快速重传+快恢复综合过程

         四种算法综合执行过程如下图所示:


总结

        流量控制是从接收方的数据接收能力出发来控制发送方的数据发送速率的;

        而拥塞控制是发送方考虑整个网络的状况而对其自身数据发送速率进行控制的。

        因此,作为数据发送方,应当同时考虑接收方的“接收窗口”rwnd以及发送方自身的“拥塞窗口”cwnd,最终发送数据时采用的“发送窗口”swnd不能大于二者的较小值。即swnd≤Min(rwnd,cwnd)

        换句话说,如果rwnd

        反之,如果rwnd>cwnd,说明此时是网络中的拥塞状况限制了发送窗口的最大值。


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • 本文介绍了Paxos的世界中关于复制日志与状态机的概念和重要性。通过存储日志来实现数据的持久化,并通过日志流来记录数据的变化,而不是直接持久化数据本身。这样做的好处是简化了持久化存储的操作,并且方便多机之间的数据同步。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 乐山市计算机学校2017—2018学年度第一学期开学典礼隆重举行
    乐山市计算机学校于2017—2018学年度第一学期举行了隆重的开学典礼,全体教职工和学生参加了此次典礼。乐山市计算机学校自建校以来一直秉承着追求崇高、抓住机遇、回报社会的办学宗旨,取得了累累硕果。在典礼上,常务副校长梁志明发表了致辞,鼓励全体新生用自己的智慧和勤奋去创造优秀的业绩。同时,苏稽镇派出所所长、市计算机学校法制副校长邹学斌提出了关于遵守法律法规和社会公共道德规范、树立自尊、自律、自强意识以及相信和依靠法律的建议,以维护校园秩序的平安和谐。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • 本文介绍了一个免费的asp.net控件,该控件具备数据显示、录入、更新、删除等功能。它比datagrid更易用、更实用,同时具备多种功能,例如属性设置、数据排序、字段类型格式化显示、密码字段支持、图像字段上传和生成缩略图等。此外,它还提供了数据验证、日期选择器、数字选择器等功能,以及防止注入攻击、非本页提交和自动分页技术等安全性和性能优化功能。最后,该控件还支持字段值合计和数据导出功能。总之,该控件功能强大且免费,适用于asp.net开发。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
author-avatar
大鱼
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有