计算机网络速通(四) TCP 的稳定性:滑动窗口和流速控制是怎么回事?
本文最后更新于:2024年10月8日 下午
- 这节讨论如何保证顺序的具体算法
- 以及如何在保证顺序的基础上,同时追求更高的吞吐量
TCP 作为一个传输层协议,最核心的能力就是传输,传输需要保证可靠性,还需要控制流速,这两个核心能力均由滑动窗口提供。
TCP 中每个发送的请求都需要响应,如果一个请求没有响应,发送方就会认为发送出现了故障并触发重发,大体的模型如下: 但如果和下图完全一样,每一个请求收到响应之后,再发送下一个请求,吞吐量就会很低。这样的设计会产生网络很多的空闲资源,也就是浪费带宽。带宽没有用满意味着可以发送更多的请求,接受更多的响应。
一种改进的方式就是让发送方有请求就发送出去,而不等待响应,通过这样的处理方式,发送的数据连在了一起,响应的数据也连在了一起,吞吐量也就提升了。
但是如果发送的数据非常多,成百上千个 TCP 段都需要发送,这个时候同时发送,这时候带宽可能不足,那应该如何处理?
这种情况下会考虑使用排队机制(Queuing) 考虑这样一个模型,在 TCP 层实现一个队列,新元素从队列的一端进入队列排队,作为一个未发送的数据封包,开始发送的数据封包从队列的另一端离开,可以思考一下这个模型的问题? - 这样的问题是需要多个队列,我们要将未发送的数据从队列取出,加入发送中的队列,然后将发送中的数据收到 ACK 的部分取出,放入已经收到 ACK 的队列,而发送中的封包何时收到 ACK 是一件不确定的事。这样来看,使用队列似乎存在一定的问题。
在上面的模型当中,之所以觉得算法不好设计是因为用错了数据结构。这里应该用一个叫做滑动窗口的数据结构去实现。 - 深绿色代表已经收到了 ACK 的段 - 浅绿色代表发送了,但是没有收到 ACK 的段 - 白色代表没有发送的段 - 紫色代表暂时不能发送的段
下面设计不同类型的封包顺序 - 将已发送的数据放入最左边,发送中的数据放入中间,未发送的数据放入右边,加入我们同时发送 5 个封包,也就是窗口大小等于 5,窗口中的数据被同时发送出去,然后等待 ACK,如果一个封包的 ACK 到达,那么就将它标记为已接受 这个时候滑动窗口可以向右滑动 如果发送过程当中,发送数据如果没有 ACK,那么可能会出发重传 例如,段 4 迟迟没有收到 ACK,这个时候滑动窗口就只能向右移动一个单位。这个时候如果段 4 后来重传成功,那么滑动窗口就会右移;如果段 4 还是发送失败,没能收到 ACK,那么接收方也会抛弃段 5、6、7 这样从段 4 开始之后的数据,都需要重发。
在 TCP 协议当中,如果接收方想丢弃某个段,可以选择不发 ACK,发送端发现超时后,会重发这个 TCP 段,有的时候接收方希望催促发送方尽快补发某个 TCP 段,这时可以使用快速重传能力。 - 例如:段 1、2、4 到了,但是段 3 没有到,接受方可以发送多次段 3 的 ACK,如果发送方收到多个段 3 的 ACK,就会重发段 3,这个机制称为快速重传。这个和超时重发不同,是一种催促的机制,为了不让发送方误以为段 3 已经收到了,在快速重传的机制下,接收方即便收到发来的段 4,依然会发段 3 的 ACK,而不发段 4 的 ACK,直到发送方把段 3 重传。
问题思考?
- 窗口大小的单位是多少?
- 在上面的图片当中,窗口大小是 TCP 段的数量,实际操作过程当中每个 TCP 段的大小不同,限制数量会让接收方的缓冲区不好操作,因此实际操作中窗口大小单位通常不是多少个段,而是多少个字节。
发送、接受窗口的大小可以用来控制 TCP 协议的流速,窗口越大,同时可以发送、接受的数据就越多,支持的吞吐量就越大。但是窗口越大,数据发送损失也就越大,因为需要重传的数据越多。
例子:用 RTT表示消息一去一回的时间 这时候可以提高窗口的大小来提高窗口的吞吐量,但是实际的模型会比这个复杂,因为还存在重传,丢包等等一些因素。而且实际当中,不可能真的把带宽用完,所以最终选择用折中的方式:在延迟、丢包率、吞吐量进行选择。
总结
- 有了窗口,发送方利用滑动窗口算法发送消息;接收方构造缓冲区接收消息,并发给发送方 ACK。
- 滑动窗口实现只需要少量的数组、指针即可。
面试问题
滑动窗口和流速控制是怎么回事? - 滑动窗口是 TCP 协议控制可靠性的核心,发送方将数据拆包,变成多个分组,然后将数据放入一个拥有滑动窗口的数组,依次发出,仍然遵循先入先出的顺序,但是窗口中的分组会一次性发出。窗口中序号最小的分组如果收到 ACK,窗口就会发生滑动,如果最小序号的分组长时间没有收到 ACK,就会触发整个窗口的数据重新发送。