TCP(Transmission Control Protocol,传输控制协议)是传输层核心协议,提供可靠的、面向连接的、基于字节流的数据传输服务。 RFC 793(1981)定义,后续 RFC 1323(窗口缩放/时间戳)、RFC 2018(SACK)、RFC 2581(拥塞控制)、RFC 7323(RFC 1323 的修订版)等持续增强;RFC 793 现已被 RFC 9293(2022)取代。
1. 网络拓扑
scss
┌─────────────────────────────────────────────────────────────────┐
│ TCP 端到端连接拓扑 │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 发送端 │ ←── TCP 连接 (3-way HS) ──→ │ 接收端 │ │
│ │ │ │ │ │
│ │ ┌──────┐ │ 滑动窗口机制 │ ┌──────┐ │ │
│ │ │cwnd │ │ ←── 拥塞窗口 (发送方自限) ──→ │ │rwnd │ │ │
│ │ │(拥塞)│ │ │ │(接收)│ │ │
│ │ └──────┘ │ min(cwnd, rwnd) = 实际窗口 │ └──────┘ │ │
│ │ │ │ │ │
│ │ ┌──────┐ │ 字节流语义 │ ┌──────┐ │ │
│ │ │发送 │ │ ── Seq/ACK 确认 ──────────→ │ │接收 │ │ │
│ │ │缓冲区│ │ ←── ACK + 窗口通告 ───────── │ │缓冲区│ │ │
│ │ └──────┘ │ │ └──────┘ │ │
│ └──────────┘ └──────────┘ │
│ │ │ │
│ │ IP 层(不可靠) │ │
│ └───────── 路由器/交换机 ─────────────────┘ │
│ 可能丢包/乱序/重复 │
│ │
│ TCP 在不可靠 IP 上构建可靠字节流: │
│ ① 3-way 握手建立连接 (SYN→SYN-ACK→ACK) │
│ ② 序列号标识字节位置,ACK 确认期望的下一字节 │
│ ③ 超时重传 + 快重传兜底丢包 │
│ ④ 拥塞控制 (cwnd) + 流量控制 (rwnd) 双窗口限速 │
│ ⑤ 4-way 挥手关闭 (FIN→ACK→FIN→ACK + TIME_WAIT 2MSL) │
└─────────────────────────────────────────────────────────────────┘
2. 设计哲学
TCP 的核心设计目标是在不可靠的 IP 层 之上构建可靠的字节流。这决定了它的三个根本选择:
- 面向连接:通信前握手协商初始序列号(ISN),建立双方状态。代价是 3-RTT 启动延迟(含 TLS 握手)
- 字节流语义 :不保留应用层消息边界,发送方写 1000 字节可能被拆成 3 个包,接收方读到的是连续流。这是"粘包"问题的根源------TCP 从不粘包,是应用层没有定义消息边界
- 正反馈确认:收到数据才发 ACK,不收到就不发。超时重传是兜底机制,不是主要手段
专家视角 :很多人把 TCP 当"可靠 UDP"用,这是误解。TCP 的可靠性是端到端的流可靠,不是消息可靠。如果你需要消息语义,应该在应用层实现(如 HTTP/1.1 的 Content-Length、HTTP/2 的帧边界)。
3. 报文结构(40 字节头部,含选项)
scss
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口(16) | 目的端口(16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 序列号(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 确认号(32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据偏移(4)|保留(3)|NS(1)|CWR|ECE|URG|ACK|PSH|RST|SYN|FIN |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 窗口大小(16) | 校验和(16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 紧急指针(16) | 选项(可变) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 字段 | 位数 | 专家解读 |
|---|---|---|
| 序列号 | 32 | 标识字节流位置。ISN 随机化(RFC 6528)防序号预测攻击 |
| 确认号 | 32 | 期望收到的下一个序列号,不是已收到的最后一个 |
| 数据偏移 | 4 | 头部长度,单位 4 字节。最小 5(20 字节),最大 15(60 字节) |
| CWR/ECE | 1+1 | ECN 显式拥塞通知(RFC 3168),IP 层标记 CE → TCP 回 ECE |
| URG/ACK/PSH/RST/SYN/FIN | 6 | 控制位。ACK 在连接建立后始终置 1 |
| 窗口大小 | 16 | 接收窗口通告,单位字节。窗口缩放选项可左移至 30 位 |
| 校验和 | 16 | 覆盖伪首部+首部+数据。强制(RFC 1146),校验伪首部含源/目的 IP |
| 紧急指针 | 16 | URG=1 时有效,标识紧急数据末尾。现代几乎不用 |
3.1. 关键选项
| 选项 | 说明 | RFC |
|---|---|---|
| MSS(最大段大小) | SYN 中协商,默认 536(IP MTU 576-40)。以太网通常 1460 | RFC 879(现并入 RFC 9293) |
| 窗口缩放(WSOPT) | SYN 中协商,移位数 0-14。解决高 BDP 链路窗口不足 | 7323 |
| SACK-Permitted | SYN 中声明支持选择性确认 | 2018 |
| SACK | 确认不连续块,避免不必要的重传 | 2018 |
| 时间戳(TSopt) | RTT 估算 + PAWS(防回绕序号攻击) | 7323 |
| TCP-AO | 替代 MD5 签名,防中间人篡改 | 5925 |
4. TCP 状态机(11 状态)
sql
被动 OPEN
|
v
+----------+
+---->| LISTEN |
| +----------+
| | rcv SYN
| v
| +----------+ snd SYN,ACK
| | SYN-RCVD |
| +----------+
| | rcv ACK
| v
| +----------+
| | ESTAB |<------+
主动 OPEN | +----------+ |
| | | |
v | | close | rcv SYN,ACK
+----------+ | v | snd ACK
| SYN-SENT |-------->+ +----------+ |
+----------+ rcv | | FIN-WAIT-1| |
| SYN,ACK| +----------+ |
| snd ACK| | |
| | | |
+---------------+ | |
v |
+----------+ |
|FIN-WAIT-2| |
+----------+ |
| |
| |
+----------+ | |
| TIME-WAIT|<------------------+ |
| (2MSL) | |
+----------+ |
| |
| timeout |
v |
+----------+ |
| CLOSED | |
+----------+ |
| 状态 | 专家解读 |
|---|---|
| SYN-SENT | 主动连接方,已发 SYN 等响应。SYN Flood 攻击在此状态堆积 |
| SYN-RCVD | 被动连接方,已发 SYN-ACK 等最终 ACK。半连接队列 |
| ESTABLISHED | 数据传输态。连接双方均在此态 |
| FIN-WAIT-1 | 主动关闭方,已发 FIN 等对端 ACK |
| FIN-WAIT-2 | 主动关闭方,收到对端 ACK,等对端 FIN |
| CLOSE-WAIT | 被动关闭方,收到对端 FIN,已回 ACK,等应用层 close() |
| LAST-ACK | 被动关闭方,已发 FIN,等最终 ACK |
| TIME-WAIT | 主动关闭方,收到对端 FIN+ACK 后进入,持续 2MSL |
| CLOSING | 罕见:双方同时关闭 |
4.1. TIME_WAIT 深度解读
专家视角:TIME_WAIT 是 TCP 设计中最被误解的状态。
为什么需要 TIME_WAIT(2MSL = 60s~120s)?
- 防旧报文干扰新连接:如果上一次连接的延迟报文到达,会被误认为新连接的数据。2MSL 确保旧报文已消亡
- 确保对端收到最终 ACK:如果最后的 ACK 丢失,对端会重发 FIN,本端在 TIME_WAIT 仍能响应
TIME_WAIT 的代价:
- 每个连接关闭后占用一个五元组(源IP/源端口/目的IP/目的端口/协议)2MSL
- 高 QPS 服务器端口耗尽(65535 - 1024 = ~64K 端口,2MSL=60s → 理论上限 ~1000 QPS)
生产环境优化:
tcp_tw_reuse = 1:允许新连接复用 TIME_WAIT 端口(安全,推荐,依赖时间戳防回绕)tcp_tw_recycle = 1:已废弃(Linux 4.12+ 移除),NAT 环境下会导致丢包tcp_max_tw_buckets:限制 TIME_WAIT 数量,超限直接 RST
5. 拥塞控制四算法
TCP 拥塞控制是发送方自行限制 发送速率的算法,核心变量是拥塞窗口 cwnd。实际发送窗口 = min(cwnd, rwnd)。
5.1. 慢启动(Slow Start)
- 初始 cwnd = 1~10 MSS(RFC 6928 IW10)
- 每收到一个 ACK,cwnd += 1 MSS(指数增长:1→2→4→8...)
- 增长到 ssthresh(慢启动阈值)后切换到拥塞避免
专家视角:"慢启动"不慢------它是指数增长,叫"慢"是因为相比一开始就发满窗口的方案,它"慢"了。初始 IW=10 而非 1 是 RFC 6928 的优化,减少短连接的 RTT 影响。
5.2. 拥塞避免(Additive Increase)
- cwnd 每经过一个 RTT 增加 1 MSS(线性增长)
- 持续直到检测到丢包
5.3. 快重传(Fast Retransmit)
- 收到 3 个重复 ACK → 判定丢包,立即重传(不等超时)
- ssthresh = cwnd / 2,cwnd = ssthresh + 3(3 个包已离开网络)
5.4. 快恢复(Fast Recovery)
- 不回到慢启动,而是从 ssthresh 继续线性增长
- 收到新 ACK → cwnd = ssthresh,退出快恢复
5.5. 拥塞控制算法演进
| 算法 | 特点 | 适用场景 |
|---|---|---|
| Reno | 经典四算法 | 教材基准 |
| NewReno | 改进多包丢失处理 | Linux 2.2 默认 |
| CUBIC | 基于三次函数的窗口增长 | Linux 2.6.19+ 默认,高 BDP 友好 |
| BBR | Google 提出,基于瓶颈带宽+RTT 模型 | Linux 4.9+,不靠丢包判断拥塞,长肥管道最优 |
| Vegas | 基于 RTT 延迟变化 | 公平性差,少用 |
专家视角 :CUBIC 和 BBR 的核心区别------CUBIC 仍以丢包 为拥塞信号,BBR 以带宽探测 为信号。BBR 在跨洋链路(高 RTT × 高带宽)下吞吐量可提升 10x+,但与 CUBIC 流共存时公平性较差,Google 内部全量 BBR,公网混用需谨慎。
6. 关键工程问题
6.1. Nagle 算法与 TCP_NODELAY
- Nagle 算法:小包积攒到 MSS 或收到前一个 ACK 才发送。减少小包数量
- 问题:与延迟 ACK 叠加导致额外等待(Linux 延迟 ACK 典型 40ms,Windows 可达 200ms;请求-响应模式尤其严重)
- 解决 :交互式应用设
TCP_NODELAY禁用 Nagle。HTTP/SSH/Redis 客户端默认禁用
6.2. Keep-Alive 机制
- TCP Keep-Alive 是协议可选 ,多数实现默认关闭(Linux
tcp_keepalive_time = 7200s) - 应用层 Keep-Alive(HTTP Connection: keep-alive)是另一回事
- 专家建议:用应用层心跳而非 TCP Keep-Alive,更可控、跨 NAT 友好
6.3. 粘包/拆包
- TCP 是字节流,没有消息边界------这不是 bug 是设计
- 解决方案:固定长度 / 特殊分隔符 / 长度前缀(TLV)。HTTP 用 Content-Length 或 chunked
6.4. SYN Flood 攻击
- 攻击者发大量伪造源 IP 的 SYN,服务器 SYN-RCVD 堆积,半连接队列耗尽
- 防御:SYN Cookie(服务器不分配资源,将状态编码在 SYN-ACK 的 ISN 中)+ SYN Proxy
6.5. 队头阻塞(HoL Blocking)
- TCP 层队头阻塞:前一个包丢失,后续已到达的包必须在内核缓冲区等待重传完成
- 这是 HTTP/2 over TCP 的根本痛点,HTTP/3 用 QUIC(UDP协议)解决
7. 性能调优速查(Linux)
| 参数 | 推荐值 | 说明 |
|---|---|---|
net.ipv4.tcp_tw_reuse |
1 | 复用 TIME_WAIT 端口 |
net.ipv4.tcp_fin_timeout |
30 | FIN-WAIT-2 超时(默认 60s) |
net.ipv4.tcp_keepalive_time |
600 | Keep-Alive 探测间隔(默认 7200s) |
net.ipv4.tcp_max_syn_backlog |
8192 | SYN 队列长度 |
net.ipv4.tcp_syncookies |
1 | SYN Cookie 防 Flood |
net.core.rmem_max |
16777216 | 最大接收缓冲区 |
net.core.wmem_max |
16777216 | 最大发送缓冲区 |
net.ipv4.tcp_congestion_control |
bbr | 拥塞控制算法 |
8. 应用场景
- 网页浏览(HTTP协议/HTTPS协议)
- 文件传输(FTP协议/SFTP协议)
- 电子邮件(SMTP协议/POP3协议/IMAP协议)
- 远程登录(SSH协议/Telnet协议)
- 数据库连接(MySQL/PostgreSQL)