一、TCP为何需要可靠性保障?
TCP作为互联网的"运输队长",承担着80%以上的网络数据传输任务。其核心使命是:在不可靠的IP层之上,构建端到端的可靠传输通道。
想象一下网购时商品运输需要防丢包、防损坏、防错序,TCP正是通过七大核心机制实现这一目标。
二、TCP可靠性七大支柱
2.1 数据分块与序列号机制
数据分块:将应用层数据切割为MSS(最大报文段长度,通常1460字节)
序列号:每个字节分配唯一编号(ISN初始序列号通过时钟算法生成)
# 示例:数据分块过程
app_data = "A"*5000 # 应用层数据
mss = 1460
segments = [app_data[i:i+mss] for i in range(0, len(app_data), mss)]
想象你要邮寄一本百科全书,快递员说:“抱歉,包裹太大运不了”。TCP的做法是:
- 拆书成页:把大数据拆成适合网络传输的小块(称为报文段)
- 每页编号:给每个数据块打上唯一序号(比如第1页、第2页...)
序列号的作用
- 识别顺序:接收方按序号重新组装(像拼图)
- 发现丢失:如果收到1、3号包,就知道2号丢失了
- 防止重复:如果重复收到2号包,直接丢弃
2.2 校验和双保险
首部校验和:16位反码求和,检测头部篡改
数据完整性校验:CRC32等算法(部分实现扩展)
如何验证数据完整?
发送方:计算数据的校验和(类似指纹),一起发送
接收方:重新计算校验和,对比是否一致
举个栗子🌰
你网购了一箱苹果,快递单上写着“重量5kg”。收到后:
- 称重发现只有4kg → 可能运输中丢失(校验失败,拒收)
- 重量相符但苹果腐烂 → TCP不检查内容质量(应用层负责)
2.3 重传机制四剑客

机制 | 触发条件 | 特点 |
---|---|---|
超时重传 | RTO计时器到期 | 保守策略,网络波动时效率低 |
快速重传 | 收到3个重复ACK | 效率提升30%+,避免等待超时 |
SACK | 选择性确认丢失报文 | 需双方支持,减少无效重传 |
D-SACK | 确认重复接收的报文 | 识别伪丢包,优化带宽利用率 |
超时重传
- 场景:发送包裹后开始计时,超时未收到签收就重发
- 难点:如何设置合理超时时间?(动态计算网络延迟)
![]()
超时重传机制概述
快速重传
- 场景:连续收到3个重复ACK(比如收到ACK2三次)
- 行动:立即重传2号包,不用等超时
2.4 滑动窗口流量控制
接收窗口(rwnd):接收方通过ACK通告剩余缓冲区大小
发送窗口(swnd):swnd = min(rwnd, cwnd)零窗口探测:通过ZWP技术打破死锁
2.5 拥塞控制四阶段
BBR算法(Google最新方案):基于带宽和延迟的拥塞控制
2.6 顺序保证与去重
接收端重组缓冲区管理
滑动窗口+序列号实现精准排序
2.7 全双工ACK机制
捎带确认技术(Piggybacking)
有时候,接收方在收到数据后,不用专门马上发一个确认消息给发送方,而是可以趁着自己要给发送方发送数据的时候,顺便把这个确认信息带过去,这就是捎带确认。这么做能减少网络里单独的确认消息数量,让网络用得更有效率。
累计确认与选择确认结合
在数据传输比较顺利,没有太多丢失和乱序的时候,就用累计确认,简单高效地让发送方知道一大段数据都收到了。而当出现数据丢失或者乱序的情况,接收方就用选择确认,给发送方详细地说明具体哪些数据收到了,哪些没收到,让发送方只重新发送真正丢失的数据。
三、深度剖析:TCP超时重传时间(RTO)计算
3.1 原始算法(RFC 793)
- 简单平均:
RTO = α * 平均RTT
(α=2,经验值)- 缺陷:无法处理网络抖动,容易误判
3.2 Jacobson/Karels算法(RFC 6298)
引入动态方差计算,公式:
SRTT = (1 - α) * SRTT + α * RTT_sample
RTTVAR = (1 - β) * RTTVAR + β * |SRTT - RTT_sample|
RTO = SRTT + 4 * RTTVAR
- 参数取值:α=1/8,β=1/4(通过实验优化)
- 时钟粒度:RTO最小值为1ms(现代系统)
3.3 Linux内核实现(源码解析)
// net/ipv4/tcp_input.c
void tcp_rtt_estimator(struct sock *sk, long mrtt_us)
{struct tcp_sock *tp = tcp_sk(sk);long m = mrtt_us; // 测得的最新RTT// 首次测量初始化if (tp->srtt_us == 0) {tp->srtt_us = m << 3; // SRTT = mtp->mdev_us = m << 1; // 初始方差return;}// 计算SRTT(α=1/8)m -= (tp->srtt_us >> 3);tp->srtt_us += m;// 计算RTTVAR(β=1/4)m = abs(m);if (m > tp->mdev_us)tp->mdev_us += (m - tp->mdev_us) >> 2;elsetp->mdev_us -= (tp->mdev_us - m) >> 5;// 计算RTO边界tp->rttvar_us = max(tp->mdev_us, tcp_rto_min_us(sk));tp->rto = usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
}
3.4 关键问题处理
1. 重传歧义问题
- 场景:重传后收到ACK,无法确定是对哪个包的确认
- Karn算法:
a. 重传期间暂停RTT测量
b. 使用指数退避:RTO = RTO * 2
(直到成功传输)
2. 初始RTO设置
- RFC 6298规定:初始RTO=1秒
- 实际优化:Linux默认初始RTO=1秒,最小200ms
3. 极端网络抖动
- RTO上下限:
RTO_min = 200ms
(可配置)
RTO_max = 120秒
四、大厂面试真题解析
腾讯面试题
Q:TCP如何区分流量控制与拥塞控制?
A:流量控制是端到端的接收能力限制(滑动窗口),拥塞控制是全局网络状况的响应(拥塞窗口)。二者共同决定发送窗口:swnd = min(rwnd, cwnd)
阿里面试题
Q:快重传为什么要3个重复ACK?
A:通过三重确认排除网络抖动因素:1个可能是乱序,2个可能是临时延迟,3个基本确认丢包(概率学验证)
字节跳动面试题
Q:TIME_WAIT状态过多的解决方案?
A:1. 开启tcp_tw_reuse 2. 调整tcp_max_tw_buckets 3. 应用层连接池复用
亚马逊面试题
Q:如何设计类TCP的可靠UDP协议?
设计要点:
- 添加序列号和确认机制
- 实现滑动窗口流量控制
- 构建RTT估算模块
- 实现选择性重传(SACK)
- 应用层拥塞控制(如QUIC协议)
码字不易,希望可以一键三连,我们下期文章再见!!!