TCP

参考

( TCP 可以被描述为“一种带累积正向确认的滑动窗口协议” )

头部重要的东西

P418

TCP10 image-20210323130236495
  • 序列号
  • 确认号
  • TCP Flag:包的类型,即 SYNFINACK那些
    • RST:重置连接
  • 窗口大小 Window Size:也叫 Advertised-Window
  • 校验和
  • 紧急指针:设置 URG 才有效
  • TCP 选项
    • 最常见的就是“最大段大小”( MSS ),MSS 只是 数据部分 的大小,不包含 TCP 首部,默认 536 字节( 最大 1460 ),刚好组成一个 576 字节( 20 + 20 + 536 )的 IPv4 数据报
      • 一般在发送的第一个报文段( 即 SYN 那个 )上指定这个选项
      • 要和 MTU ( 最大传输单元 )区分,只是 MSS 的设置要根据 MTU 来设置。链路层 1500 的 MTU,而去掉 TCP/IP 首部 40,就是 1460 了( 光纤是 1440 )
    • SACK 选择确认选项
    • 窗口缩放:WSCALE/WSOPT
      • 能将上面的 16 位窗口大小增加到 32 位
      • 只在 SYN 报文段中,每个方向都可不一样
    • 时间戳
      • 要求发送方在每个报文段添加 2 个 4 字节的时间戳数值,接收方会在确认中反映这些数值,允许发送方针对每个接收到的 ACK ( TCP 用一个 ACK 确认多个报文段 )
      • 14章
      • 能防回绕

连接控制

三次握手、四次挥手、半关闭、同时打开与关闭

三次握手建立

都可能带选项

  • 发送方
    • SYN + ISN(c) ( ISN:初始序列号 )
  • 接收方
    • SYN + ISN(s) + ACK ( 此 ACK = ISN(c) + 1 )
  • 发送方
    • ACK( 此 ACK = ISN(s) + 1 )

初始序列号 ISN

  • 随时间改变
  • 可视为一个 32 位的计数器,每 4 微秒 + 1,防止重叠
  • 可用于抵御伪造的 TCP
  • 现代系统采用半随机,详见协议 P428

四次挥手终止

close()

  • 主动关闭者
    • FIN + K( 当前序列号 ) + ACK( 用于确认对方最近一次发来的数据 L )
  • 被动关闭者
    • L( 序列号 ) + ACK( K + 1 )
    • 被动->主动,FIN + L( 序列号 ) + ACK( K + 1 )
  • 主动关闭者
    • K( 序列号 ) + ACK( L + 1 )

半关闭

shutdown()

  • 我已经完成数据的发送工作,并发送一个 FIN 给对方,但我仍希望接收来自对方的数据直到对方发送 FIN 给我

过程和四次挥手一样,只是被动关闭者没有发送 FIN 之前,主动关闭者还是能接收数据

同时打开与关闭

同时打开

两方同时发送 SYN

都返回 SYN + ACK

同时关闭

同时发送 FIN + ACK

都返回 ACK

和正常关闭一样,只是是交叉的

重点

ARQ 和 重传

Automatic Repeat-reQuest 自动重传请求 简单地“尝试重新发送”,直到信息最终被接收

  • 确认号( 也可以叫 ACK )
    • 只有 ACK 位字段被启用才有效( 连接建立后一般都是启用的 )
    • TCP 的 ACK 是 累积 的,即是累积确认的,发送方发了几个相同数据包,只会收到一个确认
    • 确认号代表的是这个 ACK 的发送方期待接收的下一个序列号( 即最后被成功接收的序列号 + 1 )
  • 校验和
  • 序列号( 32位 )
    • 标识了发送方到接收方的数据流的一个字节,这个字节就是该报文段( segement )中的数据的第一个字节
  • 但是一个一个发效率太低

序列号和 ACK 是以字节数为单位,所以 ACK 的时候,不能跳着确认,只能确认最大的连续收到的包,不然,发送端就以为之前的都收到了

窗口

分组窗口和滑动窗口

  • 这样的窗口结构发送方和接收方都有
    • 发送方:记录哪些分组可被释放,哪些正在等待 ACK,哪些还不能发送
    • 接收方:记录哪些分组已经被接收和确认,哪些分组是下一步期望的( 和已经分配多少内存来保存它们 ),以及哪些即使被接收也将会因内存限制而被丢弃
  • SACK
    • 因为接收的数据是无序的,窗口会出现空洞
    • 发送方要了解接收方有哪些空洞,就能重传这个分组
    • 要在 SYN 报文段开启“允许选择确认”选项
    • 两方都支持
    • SACK 保存在选项中,包含接收方已经成功接收的数据块的序列号范围,每个范围被称作 SACK 块,由一对 32 位序列号( 一共 32 位 )表示,因此,一个 SACK 选项 = n 个 SACK 块 = 8 * n + 2 字节( 多的 2 个字节保存 SACK 选项的种类和长度 )
    • 一般一个报文段最多 3 个 SACK 块( 已使用时间戳选项 )

变量窗口

( 用于流量控制和拥塞控制 )

( 窗口大小会变 )

( 一个窗口只会设置一个定时器 )

  • 为了处理接收方相对于发送方太慢的问题
  • 两种方式
    • 基于速率
      • 给发送方指定某个速率,确保数据不会超过这个速率
        • 适合流应用程序,可被用于广播和组播发现
    • 基于窗口
      • 窗口大小不是固定的,而是允许时间而变动的
      • 逻辑上,窗口更新的告知是与 ACK 分离的,但实际上两个是由 同一个分组 携带的,意味着发送方往往会在它的窗口滑动到右边的 同时 调整它的大小

重传

  • 基于时间( 计时器超时 )
  • 基于确认信息( 更有效 )( 快速重传 )

两者都有用到

重传超时RTO - Timeout

P465

  • 根据平均往返时间 RTT(一个数据包从发出去到回来的时间) 来设置 RTORTTs( RTT样本:RTT sample ),推荐的 α 值为 1/8

TCP16

  • 其他算法:Karn / Partridge 算法、Jacobson / Karels 算法等
  • 根据 时间戳 来设置
    • TSOPT
    • 协议P468

快速重传

推测 丢包

不以时间驱动,而以数据驱动重传

  • 累积确认 无法返回新的 ACK
  • 当 ACK 包含的 SACK 表明有 失序报文段

如果发送方连续三次 ( 一般来说是 3 次 ) 收到重复的 ACK,得知哪的没传到 ( 在没 SACK 的情况下,每个 RTT 内只能至多知道一个空缺 ),就会重传对应包,而不需要等到计时器超时,如果有采用 SACK,重复的 ACK 也会包含 SACK 信息

SACK

(上面也有补充)

ACK 还是快速重传的 ACK,SACK 则是汇报收到的数据碎版

image-20210323131318175

  • SACK 包含的是最近接收到的报文段的序列号范围
  • 一个 ACK 包含三四个 SACK 信息
  • 每个 SACK 包含 32 位的序列号,代表接收端存储的失序数据的 起始最后 一个序列号
  • 包含 SACK 块的 ACK 也简单称为“SACK”
SR(选择重传selective repeat)

对支持 SACK 的发送方来说

  • 合理利用 SACK 携带的信息,来重传丢失的报文段,这也叫 SR ( 选择重传 selective repeat )
    • SR 也是 ARQ 的一种实现
    • ( 除了SR,还有一个叫 GBN 回退 N 步 )
    • TCP 有两者的特性,更偏向 SR ( 选择重传 selective repeat )
  • 一般不叫 SR,会与另一个 SR 混淆
D-SACK

使用 SACK 来告诉发送方有哪些数据被重复接收了

D-SACK 使用了 SACK 的第一个段来做标志

  • 如果 SACK 的第一个段的范围被 ACK 所覆盖,那么就是 D-SACK
  • 如果 SACK 的第一个段的范围被 SACK 的第二个段覆盖,那么就是 D-SACK

引入了 D-SACK,有这么几个好处,可以更好的做网络上的流控

  • 可以让发送方知道,是发出去的包丢了,还是回来的 ACK 包丢了

  • 是不是自己的 timeout 太小了,导致重传

  • 网络上出现了先发的包后到的情况(又称 reordering )

  • 网络上是不是把我的数据包给复制了

伪超时和重传

这种伪重传主要原因是 伪超时,当然也包括失序与重复

  • 出现延迟高峰
  • 超过了计时器
  • 触发重传
  • 触发 GBN
  • 接收到重复 ACK,即失序。还有重复( 协议P486-487 )
  • 触发快速重传

拥塞控制

拥塞控制 主要包括:

  • 慢启动
  • 拥塞避免
  • 快重传
  • 快恢复

cwnd:Congestion Window

ssthresh:slow start threshold

假设当前发送方 拥塞窗口 cwnd1 个( 拥塞窗口 cwnd 的值是几,就能发送几个数据报文段,实际上书里就是设置为 1 个 MSS,具体看系统 )

接收方收到报文段后回复一个确认,发送方首次收到确认后将 cwnd 设为 2,接下来,cwnd 4、8、16……以 指数增长,这就是 慢启动

当达到 慢启动阈值 ssthresh 时,TCP 会谨慎增加 cwnd,即进入 拥塞避免 阶段,每次 cwnd 增加 1,然后分两种版本:

  1. 如果发生 超时重传,就 重新进入慢启动,即将 ssthresh 设置为 cwnd一半,新的 cwnd 设置为 1( TCP Tahoe版本 )( 已废弃 )
  2. 如果 连续有三个冗余的ACK 时,就是出现 丢包,就将相应的报文段重传( 快速重传 ),开始执行 快恢复,即将 ssthresh 设置为 cwnd 的一半,新的 cwnd 设置为新的 ssthresh ( TCP Reno 版本 ),然后拥塞避免,每次 + 1
  • 新的 cwnd 也可以设置为 ssthresh + 3
  • 也有的 快恢复 实现是将新的 cwnd 设置为 新的 ssthresh + 1

TCP19