TCP协议——大纲

笔者最近在学习 TCP 协议时,翻阅了《数据通信与网络》一书,结合网络上的资料,整理了以下笔记。

1. TCP 提供的服务

  1. 端对端通信(Process to process communication) : 接收和发送双方都有 Sending and Receiving Buffers。
  2. 数据流服务(Stream delivery service)
  3. 双工通信(Full-duplex communication)
  4. 多路复用(Multiplexing)
  5. 面向连接(Connection-oriented service)
  6. 可靠的服务(Reliable service)

2. TCP 特点 (Features)

  • 命名系统 (Numbering System) :
    • 字节数 (Byte number):
      • 每一个 byte in a segment 有一个 byte number;
      • 传输过程中的第一个 segment 中的第一个 byte 的 byte number 是随机确定的,也被称为 Initial Sequence Number (ISN);
      • 之后的所有 byte 都会在第一个 byte number 的基础上确定自己的 number。
    • 序列号 (Sequence number): 就是每个 segment 的第一个 byte 的 byte number。
    • 确认号(Acknowledgement number): 指的是接收方希望接受到的下一个 byte number。
  • 捎带确认 (Piggybacking):将返回信息延迟发送,和下一个数据传输包合并在一起发送,这样可以提高效率。

3. Segment

  1. MSS(Maximum Segment Size)
    • 确定机制:MSS 是在三次握手时协商确认的,发送端发出 SYN 报文,其中 option 选项填充的 MSS 字段一般为1460,接收端收到 SYN 报文后,会发送 SYN+ACK 报文应答,option选项填充的 MSS 字段一般也为1460;协商双方会比较 SYN 和 SYN+ACK 报文中 MSS 字段大小,选择较小的 MSS 作为发送 TCP 分片的大小。这里通 过比较,协商双方的 TCP MSS 都是1460。参考资料:TCP 协议中MSS的理解

待续

4. A TCP Connection

  • 建立连接:3次握手 (Three-Way Handshaking)
    • 1)发送端–SYN–>接收端;2)接收端–SYN+ACK–>发送端;3)发送端–ACK–>接收端。
    • ACK 不消耗序列号,而 SYN 和 SYN+ACK 需要消耗一个序列号。
    • 为什么是三次握手而不是两次或者四次?因为三次握手正好可以让接收和发送方互相确认初始序列号,而两次是不能达到互相确认,四次是多余了。
  • 断开连接
    • 三次挥手1)主动关闭端–FIN–>被动关闭端;2)被动关闭端–FIN+ACK–>主动关闭端;3)主动关闭端–ACK–>被动关闭端。
    • 半关(Half-Close)
      • 也就是常说的四次挥手,其将三次挥手中的第二步分为两步,所以有了四次挥手;
      • 1)主动端–FIN–>被动端;2)被动端–ACK–>主动端;被动端继续传输未传完的数据给主动端,主动端继续确认数据;3)被动端–FIN->主动端;4)主动端–ACK–>被动端。

5. 流量控制(Flow Control)

  • 控制过程:
    • 接收端首先确定双方窗口的大小(在三次握手中确认);
    • 当发送窗口发送数据后,接受窗口接收到数据,接收窗口会变小(left wall closes),并将变小后的窗口大小发送给发送端,发送端会据此调整窗口大小(left wall closes/right wall shrinks),并确认上一次发送的数据被接收;
    • 只有当接收端进程使用了数据之后,接收窗口才会打开(right wall opens),接着通知发送窗口也打开(right wall opens);
  • 一般情况下,需要避免发送窗口的 shrinking;
  • 窗口关闭 (Window Shutdown):若接收端在一段时间内不想接收到发送端发送的数据,可以将rwnd设置为0,从而使发送窗口变为0,即关闭发送窗口。
  • Silly Window Syndrome:
    • 现象描述:发送或接收端处理数据的速度很慢,导致每次传输的有效数据很小,这样会导致网络传输的低效。
    • 由于发送端进程制造数据慢导致的 syndrome,可用 Nagle’s Algorithm 解决,发送端在等待数据达到最大限度后或者接收到确认信息时发送segment。
    • 由于接收端进程消耗数据慢导致的 syndrome,可用:
      • Clark’s solution: 接收端收到数据后返回确认信息,但是在缓冲区能够容纳一个最大的 segment 或一半的缓冲区空余之前,保持发送窗口大小为0;
      • Delay Acknowledgment:接收端收到数据后不立即返回确认信息,在缓冲区有一定空余容量后,再返回确认信息。(这种方法可以减少网络的 trafic,但是可能导致发送端重传未确认的数据包,TCP 定义确认信息的延迟不得高于 500 ms)

6. 错误控制(Error Control)

  • 错误包括:丢失、损坏、乱序、重复。

  • Checksum:

    • 16-bit;
    • 如果一条 segment 的 checksum 不正确,则会被抛弃,并标记为丢失。
  • Acknowledgment

    • 种类
      • Cumulative Acknowledgment(ACK):
        • 确认报文,表明接收端发送想要得到的下一个序列号;
        • 这种普通 ACK 是快重传中使用的。
      • Selective Acknowledgment(SACK):
        • 用来提供乱序和重复数据包的信息,处于 TCP header 的 option 中。
        • 当发送端收到接收端返回的SACK后,就知道哪些报文是接收端已经收到的,进而将接收端没有收到的报文进行重传。注意:SACK协议需要通信双方都支持(通过三次握手明确)。参考资料:TCP 的那些事 | SACK
        • SACK 是另外一种重传的方式,相对于快重传而言。
    • 何时生成ACK?(ACK 的触发机制)
      • Rule 1:发送数据的同时要附带 ACK(因为双方有可能互为接收方和发送方),这样可以减少 traffic(捎带确认思想);
      • Rule 2:发送端发完数据后,接收到有序的 segment,此时不立即返回 ACK segment,而是开启 ACK-Delaying Timer 等待 500 ms,在等待期间若收到又一个有序的 segment 则触发 Rule 3 (直接确认后面这个 segment), 这样可以减少确认信息的发送;
      • Rule 3:当收到一个有序的 segment,而前一个 segment 还未确认时,直接返回后一个 segment 的确认信息,这样可以少发一个 ACK,美滋滋;
      • Rule 4:当接收端收到一个乱序的 segment (说明有序的 segment 可能延迟或丢失或损坏被接收端抛弃),立即返回一个 ACK,声明想要接收到的下一个 segment 的正确序列号 (Sequence Number) ;
      • Rule 5:当丢失的 segment 重传被接收,立即返回一个 ACK,确认接下来需要的正确序列号 (此时需要包括丢失期间保存的乱序 segments);
      • Rule 6:当接收到重复的 segment,立即返回一个 ACK,确认接下来需要的正确序列号。
  • 重传(Retransmission):

    • 触发条件: segment 丢失 (lost) 或者 因为损坏 (corrupted) 被接收端抛弃 (discarded);
    • RTO 重传(Retransmission Time-Out)
      • 计时时间根据 Round-trip Time (RTT) 确定;
      • 发送端对每一个 TCP 连接都会维护一个 RTO 计时器,一旦计时器到时 (Time-out),就会触发等待确认队列中的第一个 segment 的重传,然后 RTO 计时器会开始新一轮计时 (Restart);
      • 等待确认列队若清空了 (全都被确认),则计时器关闭 (Stop),等待队列加入新的待确认 segment 了,则重新开始计时器 (Start)。
    • 快重传(Fast Retransmit):
      • 当接收端收到三条一样的 ACK 时,将待确认队列中的首个 segment 重传(注意此时 RTO 计时器未到时);
      • 为什么是三条重复 ACK,而不是一条、两条或四条? 因为出现一条或两条重复 ACK 时发送端不能判断是包延迟还是丢失(丢失和损坏这里可以看成是一样的)造成的,此时延迟的概率较大,而出现三条重复 ACK 时,包丢失的概率则变得很大了,适合触发重传,如果再等到收到四条重复 ACK,响应时间则过长,所以收到三条 duplicated ACK 时更适合触发重传。
  • 乱序(Out-of-Order Segments)

    • 引起乱序的原因:有序包的丢失、延迟、损坏,都可引起乱序。
    • 接收端将乱序数据包储存起来,等待正确顺序的数据包到达。TCP 会确保进程消耗的数据是有序的。
  • 延迟(delay)和重复(duplicated) :因为 TCP 依赖于 IP,IP的运送路线不固定,所以会导致延迟,而造成丢失的假象,判定为丢失后,会触发重传,此时发送方因为延迟重复发送了 segment。接收端处理的方式简单粗暴,就是将重复的 segment 抛弃,然后根据 rule 6 发送 ACK。

  • ACK 丢失:

    • 自动修复 :因为 ACK 包含的是需要接收的下一个 segment 的序列号,所以就算一个 ACK 丢失了,后面的 ACK 依然可以告诉发送端下一个需要的序列号;
    • 触发发送端重传后接收端 ACK 重传 :因为发送端在计时器到时前未接收到 ACK,触发了重传,过程和 segment 丢失类似。
    • 死锁 :若先前接收端关闭了发送窗口,现在接收端想开启发送窗口,于是发送了一个 rwnd 不为0 的 ACK,但是这个关键的 ACK 丢失了,此时发送端和接收端都静静等待对方的数据或指令,这就形成了死锁。解决方法 :Persistence Timer。

7. 拥塞控制(Congestion Control)

拥塞控制的目标:在网络不拥塞时,加快数据传输,在检测到网络拥塞时,减缓数据传输。

  1. 拥塞窗口(Congestion Window)

    • 作用:和流量控制不同(通过 rwnd 设置,只控制收发两端的拥塞),拥塞窗口是为了避免网路中的拥塞(路由器的拥塞)。网络拥塞会导致包丢失等差错,从而导致数据重传,使网络拥塞问题将更加严重。
    • **IP 没有拥塞控制?**理论上路由器的拥塞问题属于 IP 协议的范围,但是 IP 并没有解决这个问题,所以 TCP 需要自己解决。
    • 拥塞窗口缩写(cwnd):收发窗口的大小实际同时由 cwnd 和 rwnd 属性值控制,实际窗口大小等于两个属性值中的最小值。
  2. 拥塞检测(Congestion Detection)

    • 拥塞信号一:RTO time-out,即在计时器时间内没有收到 ACK,这种情况会触发超时重传,并被视为当前网络拥塞严重的信号(Strong Congestion);
    • 拥塞信号二:Three Duplicated ACKs,即收到三个和前一个重复的 ACK(所以加上第一个总共时收到四个),这种情况会触发快重传,并被视为网络轻微拥塞的信号(Weak Congestion)
  3. 策略(Congestion Policies)

    • 慢启动(Slow Start)
      • 指数增长(Exponential Increase):传输一开始时 cwnd = 1个MSS,每当收到一个 ACK,cwnd 便会加1个MSS,因此 cwnd 会呈指数增长。
      • 作用:因为不清楚网络拥塞情况,通过由小到大渐渐增大拥塞窗口显得更为合理。
      • 慢启动上限(ssthresh):当 cwnd 的大小超过了 ssthresh,便进入拥塞避免阶段。
    • 拥塞避免(Congestion Avoidance)
      • 线性增长(Additive Increase):只有当待确认列表中所有的包都被确认后,cwnd 加1个MSS。
    • 快恢复(Fast Recovery)
      • 触发条件:快恢复在 TCP 中是可选的,旧版本中没有采用,新版本中采用了。当收到三个重复的 ACK 时,开启快恢复。
      • 意义:在触发快重传后,通过适当增大cwnd,保持网络吞吐量。如果保持或大幅度降低原有cwnd,后面的包就发不出去了,因为前面的包还需要被确认,进而导致网络震荡过于激烈。
      • 线性增长:每收到一个重复 ACK,cwnd 增长 1 个 MSS。
  4. 常见 TCP 介绍

    1. Taho

      • Taho TCP 中没有快恢复,只有慢启动和拥塞避免(老版本);
      • 无差别对待两种拥塞信号;
      • 支持快重传,不支持 SACK;
      • 机制
        • 处于慢启动阶段,若检测到拥塞信号,则将 ssthresh 设为当前拥塞窗口的一半,并重新开始慢启动(cwnd = 1 MSS);若 cwnd 超过 ssthresh,则进入拥塞避免阶段。
        • 处于拥塞避免阶段,若检测到拥塞信号,则将 ssthresh 设为当前拥塞窗口的一半,并重新开始慢启动。
      • 存在问题
        • 无论哪个阶段,遇到快重传或超时重传的情况后(在恢复丢失数据包期间),不能发送新的数据包(因为 cwnd=1),这段时间的吞吐量为0。如果利用这段时间来发送适量的新数据包,可以大大的提高丢包时的传输效率。参考材料:TCP快速重传与快速恢复机制
        • 另外,在快重传时,Taho会重传待确认的所有包,因为它使用的是普通的 ACK(不包含 SACK),也就不包含乱序、重复等信息,发送端不能判断上一个数据窗口中哪些包是丢失的(即不能判断三个重复 ACK 是哪些包返回的),而且因为快重传机制必须等到三个重复 ACK,如果只重传待确认列表的首个包,返回的ACK可能确认的是上一个数据窗口其中的一个包,此时快重传无法触发,会陷入两端长时间等待并最终超时。参考资料:TCP 的那些事 | 快速重传
    2. Reno

      • 差别对待两种拥塞信号(新版本,目前使用最多);

      • 支持快重传,不支持 SACK;

      • 机制

        • 处于慢启动阶段:
          • 若检测到超时,重新开始慢启动,ssthresh=cwnd/2,cwnd=1MSS;
          • 若检测到3个重复 ACK,进入快恢复阶段,ssthresh=cwnd/2,cwnd=ssthresh+3MSS;
          • 若 cwnd 超过 ssthresh,进入拥塞避免。
        • 处于拥塞避免阶段:
          • 若检测到超时,重新开始慢启动,ssthresh=cwnd/2,cwnd=1MSS;
          • 若检测到3个重复 ACK,进入快恢复阶段,ssthresh=cwnd/2,cwnd=ssthresh+3MSS;
        • 处于快恢复阶段:
          • 若检测到超时,重新开始慢启动,ssthresh=cwnd/2,cwnd=1MSS;
          • 若检测到1个重复 ACK,cwnd+=1MSS;
          • 若检测到新的 ACK,进入拥塞避免阶段,cwnd=ssthresh;
      • 存在的问题:在快恢复阶段,检测到新ACK就会转入拥塞避免,Reno 设计时希望这里的新 ACK 确认的是丢失包之后以及第一个重复 ACK 之前的全部包,但可能存在新 ACK 仅仅确认了前面提到的部分包的情况,即同一数据窗口中有多个包丢失。Reno 遇到这种情况还是会转入拥塞避免,因为还存在需要重传的包,还会触发快重传,继而又进入快恢复,这样会使传输效率大打折扣,因为每一次触发快重传都会使 ssthresh=cwnd/2。

    3. NewReno

      • 对 Reno 存在的上述问题进行了优化,当进入快恢复时,会记录此时待确认包的最大序列号,只有新 ACK 确认了这个最大序列号,才会退出快恢复。每收到一个新 partial ACK 时,RTO重置,cwnd=cwnd/2。
      • 不支持SACK。
      • 存在问题:一个 RTT 只能重传一个丢失包。
    4. SACK

    5. 性能分析:参考:TCP快速重传与快速恢复机制

      • Tahoe没有快速恢复机制,在丢包后,它不仅重发了一些已经成功传输的数据,而且在恢复期间吞吐量也不高。
      • 利用SACK option携带的信息,我们能够提前知道哪些数据包丢失了。NewReno每个RTT内只能恢复一个丢失的数据包,所以如果丢失了N个数据包,那么Fast Recovery就要持续N*RTT的时间,当N比较大时,这是一段相当长的时间。而SACK则没有这个限制,依靠SACK option的信息,它能够同时恢复多个数据包,更加快速和平稳的恢复。
      • 当发生同一窗口多个丢包时,SACK和NewReno最终都能够较为快速和平稳的恢复过来。而Reno则经常出现超时,然后再用慢启动来恢复,这个时候Reno的表现就如同Tahoe,会造成已接受数据的重复传送。Reno恢复期间会出现吞吐量低、恢复时间长、不必要重发数据、恢复结束后阈值过低等一些问题,严重的影响性能。

待解决

  • Reno 和 NewReno 在快恢复阶段是否会重传待确认的所有包?
  • Reno 和 NewReno 超时重传是要重传整个待确认队列吗?快重传只重传队列中第一个包?

未完待续

你可能感兴趣的:(互联网通信协议)