TCP机制概览

前言

TCP 的原始规范是 RFC793,其中的一些错误在 RFC1122中被修正

拥塞控制(RFC5681、RFC3782、RFC3517、RFC3390、RFC3168)、重传超时(RFC6298、RFC5682、RFC4015)、连接管理(RFC5482)等特性在后续一系列的 RFC 文档中也进行了补充设计

这篇只是TCP一些经典机制的概括描述, 详细了解和确认建议阅读相关RFC文档和参考资料中的图解

包结构

  • source port, destination port 四元组
  • sequence number, 解决乱序
  • acknowledge number, 解决丢包
  • flags, 状态机
  • window, 解决流量控制

建立连接和结束连接

3次握手建立连接

握手是为了: 双方初始化自己的seq number并告知对方, 然后返回ACK

ACK和SYN可以合并为一个请求

4次挥手结束连接

挥手是为了: 双方告知对方自己不再发送数据了(但还可以接收数据)

被动方的ACK和FIN不可以合并: 返回ACK表示收到了FIN, 发送FIN表示自己没有数据要发送

期间的时间段内被动方可以继续发送数据, 发完数据再发FIN

状态变化

发出自己的FIN且接收到对方的ACK后, 进入closing状态

收到对方返回的ACK后, 进入time_wait状态, 等待 2 * max segment life 之后进入closed状态

为什么要有time_wait状态而不是直接closed

1. 本次连接的报文可能有延迟到达, 在time_wait状态将本次连接的报文进行丢弃处理, 如果直接closed会把这些报文当成新连接的
2. 如果被动方没有收到主动方返回的ACK, 被动方会尝试重发FIN, 主动方在time_wait状态等待被动方重发FIN

重传机制-解决丢包

超时重传

采样RoundTripTime动态更新timeout

当前Linux使用 Jacobson / Karels 算法, 根据平滑过的SRTT和当前最新RTT计算timeout

快速重传

数据驱动而不是时间驱动: 有数据包丢失的话, 接收方重复返回连续成功接收的最后一个包的ACK, 发送方连续收到3个ACK后重发丢失的数据包, 而不是等待timeout

SACK

接收方使用新字段SACK告知发送方自己成功接收到的包序列, 能减少发送方重传的数量(节约流量)

D-SACK

出现丢包的话, 接收方告知发起方是(发起方发送的数据包丢失)还是(接收方发送的ACK丢失)还是(网络延迟)

滑动窗口

发送窗口

发送方的缓冲区构成

1. 已发送且收到ACK的数据
2. 已发送且未收到ACK的数据
3. 未发送且接收方可以接收的数据(根据接收方返回的window大小判断)
4. 未发送且接收方不可以接收的数据

接收窗口

接收方通过TCP头中的window字段告知发送方自己的缓冲区可以接收的大小(发送方不需要等收到ACK)

流量控制

避免发送方的数据填满接收方的缓存

os缓冲区/滑动窗口

窗口关闭

糊涂窗口

发送的数据包过小会浪费带宽

1. 如果是因为接收方缓冲区可以接收的数据太少, 接收方直接返回0, 等到可以接收的数据超过一定阈值再返回真实大小
2. 如果是因为发送方可以发送的数据太少, 使用nagle算法
    1. 可以发送的数据超过一定阈值再发
    2. 收到之前数据的ACK再发

拥塞控制

避免发送方的数据填满整个网络

网络发生拥塞的时候, 不能“只考虑自己,重传自己的包”

慢启动

发送方使用cwnd(congestion window)控制, 初始ssthresh = 65535

  1. 初始为N, 表示一次传N个MSS大小的数据(MaxSegmentSize, TCP定义1MSS=536byte), N跟随论文研究和Linux版本在更新
  2. 每收到一个ACK, cwnd++(每经过一个RTT, cwnd = cwnd * 2)

拥塞避免

  1. 如果(cwnd >= ssthresh), 触发 拥塞避免 , 每收到一个ACK, cwnd += 1/cwnd(每经过一个RTT, cwnd++)

拥塞发生

  1. 如果出现丢包(快速重传发现数据包丢失)(即收到3个后续包的ACK)
    1. 如果触发超时重传 ssthresh = cwnd / 2, cwnd = 1
    2. 如果触发快速重传 ssthresh = cwnd / 2, cwnd = cwnd / 2

快速恢复

4.2.1. 触发快速恢复 cwnd = ssthresh + 3

还有很多其他拥塞避免算法


参考资料

TCP 的那些事儿(上) | 酷 壳 - CoolShell

TCP 的那些事儿(下) | 酷 壳 - CoolShell

你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗?看完图解就不愁了

通过实验深入了解 TCP 连接的建立和关闭

深入理解Linux的TCP三次握手

机制应用

一文详解长连接黑洞重现和分析

为什么 Lettuce 会带来更长的故障时间?