TCP 协议

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 层 之上构建可靠的字节流。这决定了它的三个根本选择:

  1. 面向连接:通信前握手协商初始序列号(ISN),建立双方状态。代价是 3-RTT 启动延迟(含 TLS 握手)
  2. 字节流语义 :不保留应用层消息边界,发送方写 1000 字节可能被拆成 3 个包,接收方读到的是连续流。这是"粘包"问题的根源------TCP 从不粘包,是应用层没有定义消息边界
  3. 正反馈确认:收到数据才发 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)?

  1. 防旧报文干扰新连接:如果上一次连接的延迟报文到达,会被误认为新连接的数据。2MSL 确保旧报文已消亡
  2. 确保对端收到最终 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)
相关推荐
life码农3 小时前
Nginx 配置允许指定 IP 段访问:从 192.168.1.1 到 192.168.1.124 及 /24 详解
网络·tcp/ip·nginx
javaDocker3 小时前
16.5 小时,我打掉了两只「SSL 握手失败」的怪
网络·网络协议·ssl
我就是不信4 小时前
TCP 原理详解:从三次握手到拥塞控制
网络·网络协议·tcp/ip
我就是不信5 小时前
TCP/IP 网络编程:从入门到实战
网络·网络协议·tcp/ip
骑着蜗牛撵大象3277 小时前
Agent 打字机是怎么来的:SSE 与 WebSocket 打通实时响应与中间状态
网络·websocket·网络协议·agent·sse·实时通信·流式输出
Frank_refuel7 小时前
分布式RPC框架实战(二):服务端模块划分与底层设计
分布式·网络协议·rpc
吴声子夜歌7 小时前
Nginx应用与运维——Nginx代理服务应用实战(TCP/UDP代理)
运维·tcp/ip·nginx
Fcy6488 小时前
Linux下 传输层协议TCP详解
linux·网络·tcp/ip
web打印社区8 小时前
C-Lodop 提示未准备好或 WebSocket 没准备好:先让本机服务起来
javascript·网络·websocket·网络协议·pdf·html