从零写OS(四十三):TCP 重传 —— 丢包了也能传完
上一章(四十二)修了一堆 busybox 相关的 bug,让 ls/exec/wait 都能正常工作,wget_test 也能跑通 HTTP。但那时的 TCP 实现有个隐患:一旦丢包,传输就会永远卡住。这一章把重传机制做完整。 原代码的问题 ch42 的 tcp_send_raw 函数身兼数职:构造 TCP 包、发送、存入发送缓冲区、推进 snd_nxt。 static void tcp_send_raw(tcp_sock_t *s, uint8_t flags, const void *data, uint16_t dlen) { ...发包... if (dlen) { ...存 sbuf... s->snd_nxt += dlen; } if (flags & (TCP_SYN | TCP_FIN)) s->seq++; // 推进 seq if (dlen) s->seq += dlen; // 又推进一次! } 问题一:seq 和 snd_nxt 是两个字段,但都在追踪"下一个要发的序号",语义重复,而且 seq 被推进了两次(SYN/FIN 一次、数据一次)。 问题二:重传时的代码非常丑陋: uint32_t saved_seq = s->seq; uint32_t saved_nxt = s->snd_nxt; s->seq = s->snd_una; s->snd_nxt = s->snd_una; tcp_send_raw(s, TCP_PSH | TCP_ACK, rtbuf, chunk); s->snd_nxt = saved_nxt; // 手动恢复 s->seq = saved_seq; 这种"临时改状态再恢复"的方式容易出错,而且没有指数退避。 ...