TCP
( TCP 可以被描述为“一种带累积正向确认的滑动窗口协议” )
头部重要的东西
P418
- 序列号
- 确认号
- TCP Flag:包的类型,即
SYN
、FIN
、ACK
那些- 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章
- 能防回绕
- 要求发送方在每个报文段添加 2 个 4 字节的时间戳数值,接收方会在确认中反映这些数值,允许发送方针对每个接收到的
- 最常见的就是“最大段大小”( MSS ),MSS 只是 数据部分 的大小,不包含 TCP 首部,默认 536 字节( 最大 1460 ),刚好组成一个 576 字节( 20 + 20 + 536 )的 IPv4 数据报
连接控制
三次握手、四次挥手、半关闭、同时打开与关闭
三次握手建立
都可能带选项
- 发送方
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 )
- 校验和
- 计算方法和 UDP、IP、ICMP 一样( UDP校验和 )
- 序列号( 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(一个数据包从发出去到回来的时间)
来设置RTO
,RTTs
( RTT样本:RTT sample ),推荐的α
值为 1/8
- 其他算法:Karn / Partridge 算法、Jacobson / Karels 算法等
- 根据
时间戳
来设置- TSOPT
- 协议P468
快速重传
推测 丢包
不以时间驱动,而以数据驱动重传
- 累积确认 无法返回新的 ACK
- 当 ACK 包含的 SACK 表明有 失序报文段
如果发送方连续三次 ( 一般来说是 3 次 ) 收到重复的 ACK,得知哪的没传到 ( 在没 SACK 的情况下,每个 RTT 内只能至多知道一个空缺 ),就会重传对应包,而不需要等到计时器超时,如果有采用 SACK,重复的 ACK 也会包含 SACK 信息
SACK
(上面也有补充)
ACK 还是快速重传的 ACK,SACK 则是汇报收到的数据碎版
- 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
假设当前发送方 拥塞窗口 cwnd 为 1 个( 拥塞窗口 cwnd 的值是几,就能发送几个数据报文段,实际上书里就是设置为 1 个 MSS,具体看系统 )
接收方收到报文段后回复一个确认,发送方首次收到确认后将 cwnd 设为 2,接下来,cwnd 4、8、16……以 指数增长,这就是 慢启动
当达到 慢启动阈值 ssthresh 时,TCP 会谨慎增加 cwnd,即进入 拥塞避免 阶段,每次 cwnd 增加 1,然后分两种版本:
- 如果发生 超时重传,就 重新进入慢启动,即将 ssthresh 设置为 cwnd 的一半,新的 cwnd 设置为 1( TCP Tahoe版本 )( 已废弃 )
- 如果 连续有三个冗余的ACK 时,就是出现 丢包,就将相应的报文段重传( 快速重传 ),开始执行 快恢复,即将 ssthresh 设置为 cwnd 的一半,新的 cwnd 设置为新的 ssthresh ( TCP Reno 版本 ),然后拥塞避免,每次 + 1
- 新的 cwnd 也可以设置为 ssthresh + 3
- 也有的 快恢复 实现是将新的 cwnd 设置为 新的 ssthresh + 1