7.8 TCP 的那些事儿

1 minute read

TCP头格式

图片引用自TCP 的那些事儿(上)

引用自TCP 的那些事儿(上)

  1. Sequence Number即包的序号, 用于解决网络包乱序问题
  2. Acknowlegement Number即ACK, 用于确认收到包, 解决丢包问题
  3. Window(Advertised-Window), 即滑动窗口(Sliding Window), 解决流控
  4. TCP Flag, 包类型, 用于操控TCP的状态机

TCP状态机

图片引用自TCP 的那些事儿(上) 引用自TCP 的那些事儿(上)

图片引用自TCP 的那些事儿(上) 引用自TCP 的那些事儿(上)

  • TIME_WAIT的作用, 1). TIME_WAIT确保有足够时间让对端收到ACK, 如果被动关闭那方未收到ACK, 则会触发被动端重发FIN, 来回刚好是2MSL(Maximum Segment Lifetime), 2). 有足够时间让该连接不与后面链接混在一起(部分路由缓存IP包, 连接重用则延迟收到的包与新连接的包会混在一起)

TCP重传机制

  1. 超时重传机制, 超时未收到ack, 则重传timeout包或者timeout后所有的数据
  2. 快速重传机制
    • Fast Retransmit算法, 重传不以时间驱动, 以数据驱动, 优势是不需要等到timeout重传
    • SACK算法, 给发送端回报已接收数据碎片

TCP的RTT算法(Round Trip Time)

  1. RTT即数据包从发出到回来的时间
  2. 经典算法, 1). 采样RTT, 2). 平滑计算SRTT(Smoothed RTT), 3). 计算RTO(Retransmission TimeOut)
  3. Karn/Partridge算法, 采样计算RTT, 忽略重传, 但网络闪动有较大延迟, RTO不会更新, 导致误差大
  4. Jacobson/Karels算法

TCP滑动窗口

  1. Zero Window, 发送窗口变成0后, 会发ZWP包给接收方, 让接收方ACK它的Window尺寸, 一般值会设置成3次, 每次大约30-60秒, 如果3次均为0, 有的TCP实现会发RST把连接断开
  2. Silly Window Syndrome, 解决Window size小的问题, 1). 接收端引起, 使用David Clark’s方案, 收到的数据window size小于某个值, 直接ack(0)回sender, 把window关闭, 阻止sender再发送数据, 等待接收端处理数据window size大于MSS, 或者receiver buffer有一半为空, 即把window打开让sender发送数据过来, 2). 发送端引起的使用Nagle’s algorithm, 主要思想是延迟处理, 即等到window size >= MSS或是Data Size >= MSS; 收到之前发送数据的ack回包, 才回数据, 否则攒数据

TCP拥塞处理 - Congestion Handling

  1. 慢启动(cwnd全称Congestion Window), 1). 连接建好的开始先初始化cwnd = 1, 表示可以传一个MSS大小的数据, 2). 每当收到一个ACK, cwnd++, 线性上升, 3). 每过一个RTT, cwnd = cwnd * 2, 呈指数上升, 4). ssthresh(slow start threshold), 当cwnd >= ssthresh时, 即进入拥塞避免算法
  2. 拥塞避免(Congestion Avoidance), ssthresh的值是65535字节, 当cwnd达到这个值, 则1). 收到一个ACK, cwnd = cwnd + 1/cwnd, 2). 经过一个RTT, cwnd = cwnd + 1
  3. 拥塞发生, 拥塞状态时的算法, 当丢包的时候, 1).等到RTO超时, 重传数据包, ssthresh = cwnd / 2, cwnd重置为1, 进入慢启动过程, 2). Fast Retransmit算法, 收到3个duplicate ACK时开始重传, 不等到RTO超时, TCP Tahoe的实现和RTO超时一致, TCP Reno的实现是, cwnd = cwnd / 2, ssthresh = cwnd, 进入快速恢复算法 – Fast Recovery
  4. 快速恢复(Fast Recovery)
    • TCP Reno, cwnd = ssthresh + 3 * MSS(3表示确认有3个数据包被收到了), 重传Duplicated ACKS指定的数据包, 如果再收到duplicated ACKS, 那么cwnd = cwnd + 1, 如果收到新的ACK, 那么cwnd = ssthresh, 进入拥塞避免的算法(当发送端收到连续三个重复的确认时,就执行“乘法减小”算法,把慢启动上限 ssthresh 减半。但接下去不执行慢开始算法。)
    • TCP New Reno, 基于没有SACK的支持下改进Fast Recovery, 当Sender收到了3个Duplicated ACKS, 进入Fast Retransmit模式, 开发重传重复ACKS指示的包, 如果只有一个包丢了, 重传这个包后回来的ACK把整个已经被sender传输出去的数据ack回来。如果没有的话,说明有多个包丢了。我们叫这个ACK为Partial ACK。 一旦Sender这边发现了Partial ACK出现,那么,sender就可以推理出来有多个包被丢了,于是乎继续重传sliding window里未被ack的第一个包。直到再也收不到了Partial Ack,才真正结束Fast Recovery这个过程
    • FACK算法, 用于重传过程中的拥塞流控
    • 由于发送方现在认为网络很可能没有发生拥塞,因此现在不执行慢启动算法,即拥塞窗口 cwnd 现在不设置为 1,而是设置为慢启动门限 ssthresh 减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
    • 发送方的发送窗口的上限值应当取为接收方窗口 rwnd 和拥塞窗口 cwnd 这两个变量中较小的一个,即应按以下公式确定:

参考