TCP/IP第四层协议TCP(三)交互式数据流与成块数据流

toc

两种数据流

TCP的数据流中有一部分是成块数据流(数据量大),另一部分是交互式数据流(数据量小,但是小报文多),TCP以两种不同的策略来传输这两种数据流。

交互式数据流


假设像图中那样交互,就会导致每一次按键都会产生一个数组报文段,会出现四个报文段。但每个报文段的数据仅仅是1个字节,这会导致网络吞吐量不高、传输效率也不高。

经受延时的确认

接收方不立即回复ACK,而是延迟一段时间
延时确认策略:

  • 如果在这期间该方向有有数据要发送,则ACK与沿该方向的数据一起发送(捎带确认)
  • 如果没有的则等延时时间完毕后发送ACK。内核有个定时器,每隔200ms检查一次,检查时发送待发ACK
  • 当在等待发送ACK时,如果又接收到了对方发送的数据,通常情况下会立即发送对两个报文进行确认的ACK(隔一个报文确认策略)。

nagle算法

持续收集小的发送报文段而不发送,直到接收到对方发送的ACK,收到ACK后,收集的小报文段将会被一起发送。
但是下面的情况例外:

  1. 待发送报文段达到MSS
  2. 需要发送的包是FIN包
  3. 设置了TCP_NODELAY选项
  4. 发生超时(一般为200ms)

nagle是自适应的,接收方确认的快,发送方发送的就越快。
当需要实时交互时需要关闭nagle算法,通过设置TCP_NODELAY选项就可以关闭。

经受延时的确认与nagle算法

两者能减少发送报文段的个数,提高了效率和吞吐量,但都增加了时延,拉长了RTT时间。
当两者机制一起使用时,会由于相互delay而大大降低传输效率与网络吞吐率。

成块式数据流

两种流量控制方式

  1. 滑动窗口
  2. 拥塞控制

滑动窗口

滑动窗口是由接收方进行控制的一种流量控制技术。
TCP中的滑动窗口技术允许发送方,在等待对方的确认ACK到达前,连续发送多个报文段。发送方由于不用每发一个报文就停下来等待对方的确认ACK,因此有更好的传输效率。

累计应答

在使用滑动协议窗口时,接收方不必回复收到的每个报文段,而会一次性对之前收到的所有报文进行确认,这种确认方式被称为累计确认或累计应答(cumulative acknowledgment)。
在累计应答的场景下,接收方回复的ACK表示,接收方确认了ACK值以前所有报文段。

通告窗口


在滑动窗口协议中,接收方通过向发送方通告自己窗口的大小,来控制发送方的发送速度,从而达到流量控制的目的。
图中,提供的窗口(offered window)(也成通告窗口 awnd)是接收方通告的窗口,它覆盖了从第4字节到第9字节的区域,表明接收方已经确认了包括第3字节在内的数据,且通告窗口大小为6。我们知道窗口大小是与确认序号相对应的。发送方计算它的可用窗口,该窗口表明多少数据可以立即被发送。当接收方确认数据后,这个滑动窗口不时地向右移动。窗口两个边沿的相对运动增加或减少了窗口的大小。
三种窗口边沿运动术语:

  • 窗口合拢:窗口左边沿向右边沿靠近。这种现象发生在数据被发送并确认时。
  • 窗口张开:右边沿向右移动,此时允许发送方发送更多的数据。这种现象发生在接收方的进程读取了已经确认的数据并释放了TCP缓冲区后。
  • 窗口收缩:右边沿向左移动。可能发生在内存不够用时。

滑动窗口左边沿受接收方的ACK限制,当接收到一个指示左边沿向左移动的ACK时,该ACK会被认为是重复的ACK而丢弃。当左边沿到达右边沿时,则是一个零窗口,此时发送方就不能够再发送任何数据。

图中的报文段8就包含一个零窗口。174ms之后的一个ACK被称为窗口更新,因为这个ACK不对任何数据进行确认,只是向发送方通告接收方的进程已经读取了已确认的数据并释放了TCP缓冲区,现在窗口大小更新为4096字节。
零窗口后总是会跟一个窗口更新ACK。
虽然图中发送方总是发送全窗口大小的数据,但是实际上发送方也可以不这样,同样的,接收方也不必等待窗口被填满之后再发送ACK,一般情况下是隔一个报文确认。

拥塞控制

拥塞避免的四种方法:
1、慢启动
2、拥塞避免
3、快速重传
4、快速恢复

慢启动(slow start)

慢启动避免了一开始就向网络传输多个报文段而导致速率较慢的中间路由于缓存耗尽而丢失报文段的问题。慢启动技术时发送方增加了一个拥塞窗口(congestion window, cwnd)。
发送方发送数据的最大报文段大小SMSS(Sender Maximum Segment Size),取拥塞窗口与通告窗口中更小的窗口大小值来作为发送上限。
当刚建立好一个TCP连接后,拥塞窗口被默认初始化为1,表示能传输1个SMSS大小报文段数据(此时,在收到下一个ACK之前只能发一个报文段),每收到一个ACK后,就增加一个SMSS大小报文段。
发送方开始时发送一个报文段,然后等待ACK。当收到该ACK时,拥塞窗口从1SMSS增加为2SMSS,即可以发送两个报文段。当收到这两个报文段的ACK时,拥塞窗口就增加为4SMSS。这是一种指数增加的关系。如下图:

一开始发送方发送一个长度为512字节的报文段,然后等待ACK。该ACK在716 ms后收到。于是拥塞窗口增加了2个SMSS大小报文段,可以发送两个报文段了,又发送了两个报文段。当收到报文段5的ACK后,拥塞窗口增加为3SMSS。此时尽管可发送多达3个报文段,可是在下一个ACK收到之前,只能发送了2个报文段。
一个表现指数增加更明显的图:

拥塞避免

拥塞避免算法假定报文段丢失是由于源主机和目的主机之间的网络拥塞导致,而不是报文段收到损坏导致,因此需要降低在拥塞时报文段进入网络的速率。
拥塞避免算法与慢启动都是在控制拥塞窗口(cwnd),但是增加大粒度不一样,慢启动每次收到一个ACK后cwnd增加1SMSS,而拥塞避免算法每次收到一个ACK时cwnd增加1/cwnd(同时需要符合在一个RTT时间内不管收到多少ACK最多增加一个报文段),前者是指数增长,后者是线性增长。
慢启动和拥塞避免cwnd增长看起来就像这样:

前面部分是慢启动,后面部分是拥塞避免。
慢启动和拥塞避免都是发送方控制的流量控制方式。它们也通常一起实现。这时需要对每个链接维护两个变量:一个是拥塞窗口,另一个是慢启动门限ssthresh。
控制流程如下:

  1. 对于一个新连接cwnd初始化为1个SMSS,ssthresh初始化为65535字节大小。
  2. 刚开始建立连接或者拥塞时(发生超时表明的),使用慢启动,将cwnd设为1SMSS。
  3. 当发生拥塞(不论是超时表明的还是收到重复确认表明的)时,慢启动门限值ssthresh被设置为当前窗口(cwnd与接收方通告窗口中的较小者)的一半(至少为两个报文段大小)。并把cwnd重新设为1。
  4. 当收到确认,则开始增加cwnd,增加方式取决于使用的是慢启动还是拥塞避免。
    • 当 cwnd < ssthresh 时,使用慢启动,一直持续到 cwnd = ssthresh 时停止,在停止之前cwnd指数增长。
    • 当 cwnd >= ssthresh 时,使用拥塞避免,cwnd线性增长。

      图中cwnd为24时发生超时。

快速重传与快速恢复

当发送方连续收到三次对或三次以上重复的ACK,则认为是有报文段丢失(至少概率高),立即重传丢失的报文段。

此时接收方缓冲区像这样:

接收端缓存队列的窗口移动示意
https://blog.csdn.net/whgtheone/article/details/80983882

随后进入快恢复算法,即不执行慢启动而是执行拥塞避免。
进入拥塞避免而不是慢启动的原因是,当接收方接收到报文段,但发现这个报文段的序号不是自己当前需要序号的报文段时才会连续产生重复的ACK,而接收方又收到了这些重复的ACK,说明TCP两端的传输仍旧是正常的,或许不拥塞,不应该执行慢启动来突然较少数据流。
控制流程如下:

  1. 当收到第三个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半(最少为两个报文段大小)。
  2. 设置拥塞窗口cwnd设置为ssthresh + 3 * SMSS并重传丢失的报文段。
  3. 重新进入拥塞避免状态。
  4. 每收到一个重复ACK,将cwnd值增加1SMSS。
  5. 当收到一个不再重复的ACK,将cwnd重设为ssthresh。
  6. 期间如果出现超时拥塞,转为慢启动状态。




原创不易,转载请注明出处,谢谢
原文地址:https://www.cnblogs.com/Keeping-Fit/p/14190275.html