很多人一开始接触TCP/IP,眼光全粘在三次握手、四次挥手、IP头部格式这些“名场面”上。本系列前面几篇也确实把连接建立和断开讲了又讲。可等你真正到线上排查问题,麻烦往往不是握手握不起来,而是一些“软刀子”:传输文件卡几秒、小包交互延迟忽高忽低、服务端TIME_WAIT堆积如山,甚至冒出“网络适配器没有启用TCP/IP服务”或者error=10044这种让人摸不着头脑的报错。这些问题的答案,几乎都藏在TCP/IP协议栈里那些不太起眼的机制中。
这篇就把这批幕后机制逐个拆开:超时重传、流量控制窗口、拥塞控制、Nagle算法与延迟ACK、保活与半关闭、MSS/MTU边界处理,以及在协议栈异常时的排障思路。FTP、HTTP、SSH这些应用层协议看着千差万别,到了传输层之后依赖的其实是同一套TCP机制,所以搞懂了这些机制,等于给各种网络问题都配了通用的排查框架。不管你是做网络开发、运维,还是写业务代码时被socket折磨过,都能从里面找到直接能拿去用的判断方法。读完之后你会发现,TCP的“复杂”其实是在把一个复杂网络里所有可能出问题的环节都提前顾虑到了。
1. 从一次诡异的“卡顿”说起:超时重传与RTO的自适应之道
有次我调一个内网报表系统,页面加载很快,但下载几十MB的Excel时,进度条会规律性地卡住两三秒,然后又继续跑。刚开始怀疑是服务端生成报表慢,后来用tcpdump抓包才发现,TCP层在不停重传,数据包里的序列号重复出现。最后定位到交换机某个端口的CRC错误。这段经历让我意识到,很多人对TCP重传机制的理解还停留在“丢了就重传”,但重传背后的触发逻辑和自适应策略,才是真正决定网络体验的部分。
1.1 两种重传触发方式:靠ACK还是靠时钟
TCP重传并不只有“超时重传”一种,实际触发重传的路径有两条。
一条是常见的超时重传:发包后启动重传定时器,超过RTO(Retransmission Timeout,重传超时时间)还没收到ACK,就判定包丢了,重传该段。另一条是快速重传:发送方收到3个相同的重复ACK(duplicate ACK),不等RTO到期,立刻重传疑似丢失的包,并进入快速恢复流程。
快速重传之所以有意义,是因为重复ACK本身就是一种信号。接收方收到了比期望序列号更靠后的数据,但它只确认自己真正想要的那个序号,于是反复告诉发送方:“我还在等某一段,你赶紧补。”连续3个重复ACK,基本可以认定那段已经丢了。用时钟兜底,用信号加速,这是TCP兼顾可靠性和效率的典型思路。我在实际看跟踪文件时,会特别看重传模式,因为不同模式对应的故障类型完全不同:
| 触发类型 | 触发条件 | 常见原因 | 排查方向 |
|---|---|---|---|
| 快速重传 | 收到3个重复ACK | 链路随机丢包、中间设备队列溢出 | 物理链路质量、交换机丢包率 |
| 超时重传 | RTO超时未收到ACK | 单向链路阻塞、接收缓冲耗尽、RTO过小 | 网络连通性、接收端应用读取速度 |
1.2 RTO的自我进化:为什么不能用一个固定超时
RTO不能拍脑袋设一个固定值。网络往返时间会波动,局域网可能0.1ms,跨洋链路可能200ms;即便同一条链路,高峰和低谷的延迟也差很多。RTO
