家里路由器连着三台手机、两台电脑、一个智能音箱,视频会议正开着,下载还在跑,这时候如果某条消息突然‘消失’了,或者文件传到一半变花屏、打不开——问题很可能出在传输协议不够可靠。不是带宽不够,而是底层协议没把数据稳稳送到。
确认机制:发了不等于到了
TCP 为什么比 UDP 更‘靠谱’?核心之一就是 ACK(确认应答)。发送方发出一段数据后,并不立刻扔掉,而是等着接收方回一个‘我收到了,下一段请发序号1001’。如果超时没等到,就重发。这就像你微信发完一条重要消息,看到右下角出现‘✓✓’才放心;要是只显示一个‘✓’,你大概率会再戳一下‘重新发送’。
实际部署中,ACK 不是每包都回——太浪费带宽。常见做法是延迟确认(Delayed ACK):攒够两个包,或等 200ms,再统一回一次。但遇到实时音视频流,就得调低延迟,否则卡顿感明显。
序列号与乱序重组:快递拆箱也得按顺序
网络里各数据包走的路径可能不同,有的快有的慢。A 包走了主干道,B 包绕了小巷子,结果 B 先到。没有序列号,接收端只能照单全收,把‘你好’和‘世界’拼成‘世界你好’。
TCP 每个字节都有唯一序列号。接收方靠它把先到的 B 包暂存缓冲区,等 A 包一到,立刻按序组装。路由器本身不参与这个过程,但它转发时不能篡改 IP 头里的标识字段,否则上层 TCP 校验就会失败,触发重传。
滑动窗口:别一股脑全塞进来
想象快递员扛着 50 箱货冲进你家门——你根本没地方堆。可靠传输也一样:接收方内存有限,不能让发送方无脑狂发。滑动窗口就是双方动态协商‘我现在还能收多少’。
窗口大小写在 TCP 头里,接收方每次 ACK 都会更新它。比如当前窗口是 4KB,发送方最多发 4KB 就得停;等收到新 ACK 告诉窗口变成 6KB,才能继续发。家用路由器默认不做窗口调节,但如果你用 OpenWrt 刷机,可以在 /proc/sys/net/ipv4/tcp_rmem 手动调接收窗口最小/默认/最大值:
echo '4096 65536 8388608' > /proc/sys/net/ipv4/tcp_rmem
校验和:防错不防伪
TCP 校验和覆盖伪头部+TCP头+数据,能发现传输中因线路干扰导致的比特翻转。比如 0x5A 变成 0xDA,校验和立刻对不上,包直接丢弃,避免脏数据污染应用层。
注意:它不加密、不防篡改,只是基础完整性检查。企业级设备常配合更严的 L4/L7 检测,但家用路由一般只做透传,这部分靠终端系统自己扛。
超时重传:等多久是个技术活
等 ACK 的时间不能拍脑袋定。RTT(往返时延)波动很大:深夜 20ms,晚高峰可能飙到 300ms。TCP 用 RTT 采样 + 平滑算法(如 Jacobson/Karels 算法)动态算出 RTO(重传超时时间)。Linux 内核里这段逻辑藏在 tcp_rtt_estimator() 函数里。
如果你手动抓包看 Wireshark,会发现 SYN 包的 RTO 初始值通常是 1 秒,之后越跑越准。有些老旧路由器固件 RTO 固定设为 3 秒,一遇抖动就猛重传,反而加剧拥塞。