Linux 网络编程:传输层 TCP(二)—— TCP 可靠传输机制详解
在上一篇《Linux 网络编程:传输层 TCP(一)》中,我们介绍了 TCP 的基本概念、三次握手与四次挥手。本篇将深入讲解 TCP 的核心——可靠传输机制,包括确认应答、序列号、重传机制、滑动窗口、流量控制和拥塞控制等内容。
一、TCP 为什么可靠?
TCP(Transmission Control Protocol)是面向连接、可靠传输的协议。
相比 UDP:
| 特性 | TCP | UDP |
|---|---|---|
| 是否连接 | 是 | 否 |
| 是否可靠 | 是 | 否 |
| 数据顺序 | 保证 | 不保证 |
| 流量控制 | 支持 | 不支持 |
| 拥塞控制 | 支持 | 不支持 |
TCP 能够保证:
- 数据不丢失
- 数据不重复
- 数据按顺序到达
- 数据完整性验证
二、序列号(Sequence Number)
TCP 发送数据时,会给每个字节编号。
例如:
客户端发送:
Hello TCP
转换为字节:
H e l l o T C P
1 2 3 4 5 6 7 8 9
TCP 会维护一个序列号:
Seq = 1000
发送:
Seq=1000
Length=9
表示:
1000~1008
这些字节的数据已经发送。
序列号作用
用于:
1. 数据排序
假设:
包1 Seq=1000
包2 Seq=2000
包3 Seq=3000
网络中:
包2先到
包1后到
包3最后到
TCP 根据序列号重新排序:
1000
2000
3000
应用层看到的数据仍然正确。
2. 检测丢包
例如:
1000
2000
4000
发现:
3000 丢失
TCP 自动请求重传。
三、确认应答机制(ACK)
TCP 每收到数据:
都会发送确认报文。
例如:
客户端 → 服务端
Seq=1000
Length=100
服务端收到后:
ACK=1100
表示:
1000~1099
已经收到
下一次请从:
1100
开始发送。
ACK 的意义
确认:
我已经收到你发送的数据
避免:
发送方不知道数据是否成功到达
四、超时重传机制
如果发送方迟迟收不到 ACK:
Seq=1000
发送后:
ACK未返回
TCP 认为:
数据丢失
于是:
重新发送
这就是:
Retransmission
重传机制。
超时时间(RTO)
TCP 不会立刻重传。
而是等待:
RTO
Retransmission Timeout
例如:
200ms
超过:
200ms
还没 ACK:
重传
动态调整
网络快:
RTO变小
网络慢:
RTO变大
Linux 内核自动计算:
RTT
Round Trip Time
动态调整超时时间。
五、快速重传(Fast Retransmit)
传统重传:
等超时
效率较低。
TCP 增加:
快速重传
例如:
1000
2000
3000
4000
其中:
2000丢失
收到:
1000
3000
4000
接收方持续发送:
ACK=2000
ACK=2000
ACK=2000
连续三个重复 ACK:
Duplicate ACK
发送方立即判断:
2000丢失
无需等待超时:
直接重传
提高效率。
六、滑动窗口机制(Sliding Window)
如果:
发一个包
等ACK
再发一个包
效率极低。
TCP 引入:
滑动窗口
工作原理
例如窗口大小:
5000字节
发送方可连续发送:
1000
2000
3000
4000
5000
无需等待 ACK。
接收:
ACK=3000
表示:
前3000字节收到
窗口向前滑动:
3001~8000
继续发送。
窗口示意图
已确认 | 已发送未确认 | 可发送 | 不可发送
--------|-----------|------|-------
随着 ACK 到来:
窗口不断向前移动
因此称为:
Sliding Window
七、流量控制(Flow Control)
问题:
发送方速度快。
接收方速度慢。
例如:
发送:
100MB/s
接收:
10MB/s
接收缓冲区很快满了。
TCP 解决方案
接收方告诉发送方:
Window = 4096
表示:
还能接收4096字节
发送方最多发送:
4096字节
缓冲区满
接收方:
Window = 0
表示:
别发了
发送方暂停。
缓冲区恢复
接收方:
Window = 2048
发送方继续发送。
八、拥塞控制(Congestion Control)
流量控制:
防止接收方崩溃
拥塞控制:
防止网络崩溃
网络拥塞
例如:
100台主机
同时发送:
10Gbps
交换机:
只有1Gbps
结果:
大量丢包
网络拥塞。
九、慢启动(Slow Start)
TCP 不知道网络能力。
开始时:
小流量发送
拥塞窗口:
cwnd = 1 MSS
例如:
1460字节
每收到 ACK:
窗口翻倍:
1
2
4
8
16
32
64
指数增长。
示意图
时间 →
1
2
4
8
16
32
64
快速探测网络带宽。
十、拥塞避免(Congestion Avoidance)
达到阈值:
ssthresh
后:
不再指数增长。
改为:
线性增长
例如:
64
65
66
67
68
避免网络突然拥塞。
十一、TCP 拥塞控制过程
完整流程:
慢启动
↓
达到阈值
↓
拥塞避免
↓
发现丢包
↓
快速重传
↓
快速恢复
↓
继续拥塞避免
现代 Linux 默认使用:
- CUBIC
- BBR
作为拥塞控制算法。
十二、TCP 发送与接收缓冲区
Linux Socket 内部:
应用程序
↓
发送缓冲区
↓
TCP
↓
网络
接收端:
网络
↓
TCP
↓
接收缓冲区
↓
应用程序
查看缓冲区:
sysctl -a | grep tcp
或:
cat /proc/sys/net/ipv4/tcp_rmem
cat /proc/sys/net/ipv4/tcp_wmem
示例:
4096 87380 6291456
表示:
最小值
默认值
最大值
十三、Linux 中 TCP 状态查看
查看连接:
ss -ant
或者:
netstat -ant
结果:
ESTAB
LISTEN
TIME_WAIT
CLOSE_WAIT
查看 TCP 统计:
ss -s
输出:
TCP:
estab 15
closed 200
timewait 20
十四、面试高频问题
TCP 为什么可靠?
答:
- 序列号机制
- ACK确认机制
- 超时重传
- 快速重传
- 滑动窗口
- 流量控制
- 拥塞控制
TCP 为什么比 UDP 慢?
因为 TCP 需要:
- 建立连接
- 维护状态
- ACK确认
- 重传机制
- 拥塞控制
额外开销较大。
滑动窗口有什么作用?
提高吞吐量。
允许:
连续发送多个数据包
而不必等待每个 ACK。
流量控制与拥塞控制区别?
流量控制:
发送方 ↔ 接收方
关注:
接收能力
拥塞控制:
发送方 ↔ 网络
关注:
网络承载能力
总结
TCP 之所以能够成为互联网最重要的传输协议,核心原因在于其完善的可靠传输机制:
- 序列号保证数据有序。
- ACK 保证数据已收到。
- 超时重传解决丢包问题。
- 快速重传减少等待时间。
- 滑动窗口提高传输效率。
- 流量控制保护接收端。
- 拥塞控制保护整个网络。
理解这些机制后,再学习 Linux Socket 编程中的 send()、recv()、epoll()、高并发服务器设计时,会更容易理解 TCP 在内核中的工作原理。