0%

网原 6.2 传输控制协议

传输控制协议 TCP,把网际层不可靠的数据报传输服务(IP协议),转化为了可靠的面向连接的传输服务。

6.2.1 TCP服务模型

TCP 服务的特点:

  • 面向连接的传输:传输前先建立连接,传输完毕后释放连接。
  • 端到端通信(port - port):TCP不支持广播和组播,只能是1对1的。
  • 高可靠性:TCP段顺序到达,即使出现丢失也必然重传(逻辑上视为不会丢失)
  • 全双工传输:发方和收方建立连接后,建立了对称的两条路径,可以实现双向传输。
  • 采用字节流方式:以字节为单位,传输字节序列。

字节流方式:

todo

6.2.2 TCP数据传输机制

1.TCP的段结构

TCP用于大数据量传输,应用层把很长的报文交给传输层后,传输层将其分段,称为TCP报文段

TCP报文段的结构如下图:

最大报文段长度, MSS

MSS(maximum segment size): TCP段的众多选项之一。

MSS 是:在某个TCP连接中,端系统(主机/网关)能够接收的TCP段的数据部分的最大长度。单位字节。

TCP在三次握手中,每一方都会建议对方:发来的TCP段采用建议的MSS值。如果对方不同意该期望值,则对方会采用默认值。(IPv4, 576-20-20 = 536 bytes, IPv6 = 1220)

同一连接中,不同方向的TCP段可以有不同的MSS值。

MSS 长度并不包括TCP段的头部长度,和,IP 分组的头部长度。

IP层具有一个与MSS相似的概念---MTU(Maximum Transfer Unit)。

下图反映了 MSS与MTU 的关系:

MSS值太小或太大都不合适。
太小,例如MSS值只有1byte,那么为了传输这1byte数据,至少要消耗20字节IP头部+20字节TCP头部=40byte,这还不包括其二层头部所需要的开销,显然这种数据传输效率是很低的。
过大,导致分组封装很大,那么在IP传输中分片的可能性就会增大,接受方在处理分片包所消耗的资源和处理时间都会增大。如果分片在传输中还发生了重传,网络开销还会进一步增大。
因此合理的MSS是至关重要的。MSS的合理值应为保证分组不分片的最大值。对于以太网MSS可以达到1460byte.

RTT,往返时延

往返时延 RTT(Round-Trip Time) 或 RTD(Round-trip Delay Time) : 一个信号从源端到目的端的时间 + 该信号的应答信号从目的端返回源端的时间。

传输轮次:发出一个TCP段,收到一个TCP确认段。

6.2.6 TCP拥塞控制

从整体上来讲,TCP拥塞控制窗口变化的原则是AIMD(the Additive-Increase/Multiplicative-Decrease),即加法增大倍数减小。

AIMD原则可以较好地保证流之间的公平性,一旦某个连接出现丢包,那么立即将其 ssthresh 减半 将 cwnd退避到1 mss,给其他新建的流留有足够的空间,从而保证整个传输的公平性。

拥塞窗口 cwnd

拥塞窗口,cwnd, congestion window。

每个TCP连接中的发送方都有一个拥塞窗口,用于限制“待发而未确认的段”的数量。

拥塞窗口不等于TCP段中的win窗口——滑动窗口(rwnd)!
滑动窗口是由接收方维护的接收窗口,用于在ACK确认段中告知发送方:你还可以发送多少个报文段。
而拥塞窗口是发送方维护的发送窗口,它与接收方的滑动窗口和整个网络当前的拥塞状态都有关系。比如:有可能接收方的滑动窗口很大,但当前网络发生了拥塞,导致发送方的拥塞窗口(发送窗口)只能取很小的一个值。

发送方确定根据拥塞窗口和接收方的接收窗口,来确定发送窗口: \(发送方的发送窗口 = min(拥塞窗口 cwnd,接收方窗口 win)\)

拥塞窗口会随着接收窗口、网络的拥塞状态而变化。
假设接收窗口足够大,发送方就会根据网络拥塞状况来调整拥塞窗口的大小:

  • 只要网络没有出现拥塞,就把拥塞窗口增大一些,以便把更多的TCP段发送出去。
  • 如果出现拥塞,就减小拥塞窗口,减少注入到网络中的TCP段。

尽管拥塞窗口的单位是字节,为了减少复杂度,理解算法的核心内容,讨论中的拥塞窗口数就用 MSS(Sender Max) 作为单位。

慢启动(Slow Start),cwnd 指数增大

慢启动:TCP连接的几种拥塞控制策略之一。

发送方刚开始发送数据时,并不清楚网络当前的拥塞情况,如果立即注入大量TCP段到网络中就可能引起拥塞,较好的方法是由小到大逐渐增大发送窗口(即拥塞窗口)。

在开始时,设置 cwnd = MSS * 很小的倍数,假设为 1 MSS
在每收到一个确认段后,把拥塞窗口扩大一倍。(这里有很多细节,简单理解为翻倍即可)。

在发生下列事件前,拥塞窗口会迅速的增大,数据传输率也相应的增大。
但发生下列事件时,拥塞窗口就会减小:

  • TCP段丢失(被中间节点丢弃,说明发生了拥塞):此时就需要减少注入网络的负载。
  • 接收方通知:接收窗口不允许继续发送了:调整拥塞窗口,甚至停发等待接收窗口变大。
  • cwnd = ssthresh ,拥塞窗口达到慢启动门限了:停止慢启动过程(指数增大),进入拥塞避免阶段(线性增大)。
慢启动门限 ssthresh

慢启动的 cwnd 可以很快的增长,从而尽快最大程度的利用网络带宽,但 cwnd 不能一直无限增大,否则会发生拥塞。

TCP设置了一个变量:慢启动门限,ssthresh(slow start threshhold) 。

大多数TCP实现中,ssthresh = 65536 byte。

根据是否发生重传,来判断拥塞

TCP如何判断网络是否发生了拥塞呢?通过是否发生了重传来判断。

我们直到,重传可能发生在2种情况下:

  • 段丢失:因为拥塞,中间节点资源不足,只能丢弃段。每一个发出的TCP段都有一个重传定时器(RTO,Retransmission Time Out),若直到RTO超时都尚未收到ACK确认段,那么发方就应对该报文段进行重传。
  • 段错误:因为位错误,无法通过校验,节点将其丢弃,不发ACK确认段,time out后导致重传。

在某些情况下,比如无线网络中,传输错误确实可能发生,但相对来说,现在的网络,尤其是有线网,错误率非常低,因此一旦发生重传,则可以理解为就是发生了拥塞。

事实上,除了超时,后面会讲到的3 ACK,也会导致重传,它同样属于拥塞。因此把 重传 视作拥塞。

cwnd>=ssthresh,进入拥塞避免(congestion avoidance),cwnd 加法增大

一旦 cwnd >= ssthresh ,就结束慢启动阶段,进入拥塞避免阶段。

拥塞避免算法:
每次收到ACK确认段后,cwnd 只增加 1 个报文段的长度。
cwnd = cwnd + 1 MSS

( 具体实现有差异,有的版本是 cwnd = cwnd + 1mss / cwnd, 但都是线性递增的)

注意:
TCP连接中,发送方并不是从应用层每收到一个段就立即发送出去,而是可以积累到一定量后再发。
接收方也并不是每收到一个段,就立即发回ACK确认信息,同意可以对多个段只确认一次,只要ACK没有超时,这多个段就算接收成功了。

cwnd的值随着传输轮次线性增加,逐步调整发送窗口,在“最大化利用带宽”和“发生拥塞”之间寻找平衡点,避免了cwnd 增长过快导致网络拥塞很快发生的情况。

在拥塞避免阶段,随着 cwnd 的增大,最终也会发生拥塞,表现为确认段的超时,此时:

  1. ssthresh = cwnd / 2
  2. cwnd =1 MSS
  3. 开始慢启动阶段
快速重传, fast retransmit

通常情况下,TCP发送方使用一个计时器来识别段丢失:如果超过RTT时间没有收到该段的确认段,则认为该段已经丢失,应重传该段。

而快速重传则使得发送方无需等待RTT时间,即刻判断段丢失,从而更快的重传丢失段。

快速重传机制利用了TCP段的发方序列号和收方确认号的关系:
发方TCP段带有序列号 SEQ(sequence number),收方确认段带有确认号ACK(acknowledgement number)。

确认号的意义:收方告知发方,“序列号 < 确认号”的所有段都已正确接收,期待发送“序列号=确认号”的段。

假设,收方收到SEQ=1的段,随即返回 ACK = SEQ + 1 = 2的确认段,通知发方:SEQ=1的段已经接收,请发送SEQ=ACK=2的段!
再假设,发方发出了SEQ=2,3,4的3个TCP段,却都因为拥塞在网络中丢失了,而随后SEQ=5,6的段却抵达了收方。

收方对SEQ=5的段发送确认段时,按照ACK的含义,只能令ACK=2(即SEQ=1已正确接收,期待发送SEQ=2的段)。同理,SEQ=6的段的确认段也只能令 ACK=2。

这样一来,发方就会收到2个“ACK=2的确认段”,即“重复的确认段”。
一旦出现这种情况,发方就可以判断出:SEQ=2的段丢失了!此时无需继续等待 SEQ=2的段的确认段超时,可以立即重发 SEQ=2的段,SEQ =3,4 的段同理。这就是快速重传!

在某些实现中,发方收到3个相同ACK的确认段,即可立即重传SEQ=ACK的段。

如果在任意阶段发生了段丢失,

注意:段丢失发生在慢开始阶段,或拥塞避免阶段,都会触发 TCP Tahoe/ Reno #### TCP Tahoe

  1. 快速重传丢失的段
  2. ssthresh = cwnd / 2
  3. cwnd = 1 MSS
  4. 开始慢启动

TCP Reno,快速恢复

  1. ssthresh = cwnd / 2
  2. cwnd = cwnd / 2 + 3 = ssthresh + 3
  3. 快速重传丢失的段
  4. cwnd = ssthresh
  5. 开始拥塞避免(线性递增 cwnd)

快速恢复的“数据报守恒”原则

快速恢复的思想是“数据包守恒”原则:
同一个时刻在网络中的数据包数量是恒定的,只有当“老”数据包离开了网络后,才能向网络中发送一个“新”的数据包,如果发送方收到一个重复的ACK,那么根据TCP的ACK机制就表明有一个数据包离开了网络,于是cwnd加1。

如果能够严格按照此原则,那么网络中很少会发生拥塞,事实上拥塞控制的目的也就在修正违反该原则的地方。

具体来说快速恢复的主要步骤是:

  1. 当收到3个重复ACK时,把ssthresh设置为cwnd的一半(ssthresh = cwnd /2),把cwnd设置为ssthresh的值加3 ( cwnd = cwnd /2 + 3 ),然后重传丢失的报文段。
    加3的原因是因为收到3个重复的ACK,表明有3个“老”的数据包离开了网络。
  2. 如果再收到重复的ACK,拥塞窗口增加1(如果丢失的段后有多于3个段抵达收方,或者快速重传的段被确认了,就还会收到重复的ACK)。
  3. 当收到新的ACK确认段时,把cwnd设置为第一步中的ssthresh的值(cwnd = ssthresh)。如果确认了新的段,说明快速重传的段已确认,进入拥塞避免状态。
实例讲解

下图中:

  • 建立连接时,ssthresh = 16,cwnd = 1
  • 点1之前是慢开始阶段,cwnd 增长很快,每经过一个轮次就翻倍,
  • 当到达点1 时,进入拥塞避免,cwnd 每经过一个轮次加一,线性增长
  • 到达点2时,网络发生拥塞,出现确认段 time out,此时将 ssthresh = cwnd /2 ,cwnd =1, 开始慢启动阶段,即点2-点3的阶段
  • 点2-点3的慢启动阶段,cwnd 倍增,直到 cwnd >= ssthresh,
  • 点3开始进入拥塞避免
  • 点4,出现段丢失(可能是拥塞,也可能是网络错误,但都视为拥塞),此时采用快速恢复手段,ssthresh = cwnd /2, cwnd = cwnd +3, 快速重传丢失段,cwnd = ssthresh, 开始拥塞避免
  • 点5,快速恢复后的拥塞避免阶段。

到达点2的时候,网络发生拥塞了,网络出现了超时。就将 ssthresh 设置为 cwnd 的一半,并将 cwnd 重新设置为1 ,重新开始慢开始算法。到点3 时,达到了阈值,继续采用了拥塞避免算法。

-------------本文结束,感谢您的阅读-------------