Lcccy15头像
关注

第 8 天|丢了一段数据,TCP 怎么把它补回来?

上一篇,我们看了 TCP 怎样建立和关闭连接。但连接建立成功,只说明双方准备好了。数据真正开始传送后,麻烦才会出现:有的包丢了,有的晚到了;接收方读得慢,发送方却还想继续发。

TCP 要把这些情况处理好,靠的是三件事:给字节编号、确认收到的位置、控制尚未确认的数据量。我们先跟着一段丢失的数据走一遍。

TCP 编号的不是“第几个包”

假设服务器正在给浏览器发送网页内容。建立连接后,它要发送 3000 字节数据,为了便于观察,分成三段,每段 1000 字节:

发送的内容第一个字节的序号包含的字节序号
第一段10011001~2000
第二段20012001~3000
第三段30013001~4000

TCP 序号标记的是字节在数据流中的位置。表里的每段恰好 1000 字节,只是为了好算;真实网络中,每段大小不一定相同。

浏览器收到第一段后,可以回复:

ACK = 2001

意思是:“到 2000 为止的字节我都收到了,接下来请从 2001 开始。”

这叫累计确认。ACK 写的是“下一个期望收到的序号”,不是“我刚才收到了编号为 2001 的包”。

现在,让第二段在路上丢失。

后面的数据先到了,能不能当作没事?

假设第三段顺利到达。浏览器眼前有:

已收到:1001~2000
缺失:  2001~3000
已到达:3001~4000

即使它暂时保存了第三段,连续的数据仍然只到 2000。因此,它的累计确认号仍是:

ACK = 2001

服务器如果继续收到相同的确认号,就知道接收方一直在等从 2001 开始的内容。现代 TCP 还可能使用 SACK(选择性确认) 告诉发送方:“虽然中间有缺口,但 3001~4000 这一块我已经收到了。”这样发送方能更清楚地知道哪些部分需要补。

为什么不直接把第三段当成成功交给浏览器?因为上一篇说过,TCP 向应用提供的是有序的字节流。网页程序不能在缺少中间一块的情况下,假装收到了完整且连续的内容。

服务器什么时候重传?

TCP 有两类常见线索。

一种是等待超时:某段数据发出后,一直没有得到足以确认它的回复,发送方会重传。等待时间不是随手写死的常数,TCP 会参考测到的往返时间及其变化来安排重传计时。

另一种是重复确认:后续数据不断到达,接收方却反复报告“我还在等 2001”。在经典的快速重传机制中,发送方收到足够的重复 ACK 后,可以不等超时,提前重传疑似丢失的部分。

如果服务器补发的 2001~3000 到了,而浏览器还保存着 3001~4000,连续数据就接上了。浏览器此时可以把累计确认推进到:

ACK = 4001

从服务器的角度看,“前面三段都已收到”只需要这个确认号表达,不必要求浏览器分别回三张收据。

不过,没有收到 ACK,不一定意味着数据本身丢了。也可能是数据到了、确认回复却丢了。发送方可能因此重传已送达的数据;接收方依靠序号识别重复内容,不把它再当作新的字节交给应用。

到这里,TCP 的“可靠”有了具体含义:它通过确认、重传和去重,努力把字节按顺序交付。它并不能让物理网络不丢包;如果连接持续无法通信,应用最终仍会遇到失败。

为什么不发一段、等确认,再发下一段?

这样做最容易理解,却可能很慢。

假设服务器发出 1000 字节后,必须等浏览器回复才能发下一段。若两地往返时间是 100 毫秒,服务器大部分时间都在等消息,明明链路还能传更多数据,却没有充分使用。

TCP 因此允许发送方在等待确认时,继续发送一定数量的数据。可以把它想成一个不断向前移动的窗口:

已经确认的字节 │ 已发出但尚未确认的字节 │ 暂时还不能发的字节
              ↑                      ↑
            窗口左边                 窗口右边

确认号向前推进,旧数据退出窗口,新数据就能进入。这就是滑动窗口的基本思路。

但窗口不能无限大。限制它的,有两个不同的问题。

第一种限制:接收方装得下吗?

浏览器所在的电脑需要为收到的数据留出缓冲空间。如果应用读取较慢,缓冲区会逐渐被占用。

接收方会告诉发送方自己目前还能接收多少,这个值通常称为接收窗口,常记作 rwnd。发送方不能不顾这个通告,持续把数据压过来。

这叫流量控制。它保护的是通信的另一端:

接收方处理不过来,就让发送方放慢,别把接收缓冲区塞满。

假设接收方通告自己还有 4000 字节空间,发送方就不能仅凭“我的网速很快”,无视这个限制,一口气让大量未确认数据涌过去。

第二种限制:路上的网络吃得消吗?

接收方有空间,也不代表沿途网络畅通。

数据要经过多个路由器和链路。如果发送方一下投入过多数据,中间设备可能排队、延迟增大,甚至丢包。于是 TCP 发送方还会维护一个拥塞窗口,常记作 cwnd,根据观察到的网络反馈调整发送量。

两种窗口分别在问:

限制关心什么保护谁
接收窗口 rwnd对方还接得下多少?接收方
拥塞窗口 cwnd眼下网络适合放行多少未确认数据?传输路径

发送方允许保持在途、尚未确认的数据量,会受到两者中更严格的那个限制。直观地说:对方能收,路上也得能送。

经典的拥塞控制会先谨慎探测可用容量,再随着确认逐步增加发送量;观察到丢包等拥塞线索时,则降低发送强度。实际系统采用的拥塞控制算法不止一种,具体怎样增减窗口会有差别。也不要把“丢包”机械地等同于“必然拥塞”:无线干扰等问题同样可能造成丢包。

抓包时该看什么?

如果你完成了昨天的 Wireshark 练习,可以继续观察同一条 TCP 流。即使应用内容使用 TLS 加密,TCP 的序号、确认号等信息仍可用于分析连接。

选一条装载数据的 TCP 报文,记录三个值:

Seq:这段数据从哪个序号开始
Len:这段有多少字节 TCP 数据
Ack:这一端期待从对方收到哪个序号

先只看一个方向:如果某段显示 Seq=1001、Len=1000,那么对方完整收到这段及之前数据后,累计确认至少应推进到 2001。它不一定在紧接着的下一行回复;确认可能延迟,也可能一次确认多段数据。

你还可以在 Wireshark 的显示过滤器里输入:

tcp.analysis.retransmission

如果出现结果,点开看相同序号范围的数据是否再次出现。这个标签是 Wireshark 根据抓到的数据作出的分析;抓包不完整时,它也可能判断不准。没有搜到重传同样正常,说明这次练习未必碰上丢包。

现在回头看开头那段缺失的 2001~3000:序号让双方知道缺的是哪一段;ACK 告诉发送方连续收到了哪里;重传负责补缺口;两个窗口决定同时可以把多少数据放在路上。 这四件事接在一起,才是 TCP“可靠传输”的日常工作。

下一篇,我们换一个问题:TCP 已经能把字节送到服务器,但浏览器输入的 example.com 是怎样变成服务器地址的?DNS 查询要从哪里开始?

参考资料:TCP 规范 RFC 9293、TCP 拥塞控制 RFC 5681、选择性确认 RFC 2018、Wireshark TCP 字段说明。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2401_87974420/article/details/167130965

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--