文章目录
- TCP的流量控制
- TCP的流量控制方法
- 滑动窗口机制
- 持续计时器
TCP的流量控制
当 TCP 客户端持续发送大量数据时,应用程序可能正忙于其他任务,并不一定能够立刻取走数据,这会造成接收方接收缓存的溢出,导致数据丢失。
TCP 为应用程序提供了流量控制(Flow Control)机制,以解决因发送方发送数据太快而导致接收方来不及接收,造成接收方的接收缓存溢出的问题。
流量控制的基本方法:接收方根据自己的接收能力(接收缓存的可用空间大小)控制发送方的发送速率。
TCP的流量控制方法
TCP 的流量控制方法旨在确保发送方和接收方之间的数据传输速度合理、稳定,避免发送方发送速度过快导致接收方无法处理和接收数据。
以下是 TCP 流量控制的主要方法:
-
滑动窗口机制:
-
TCP 中的滑动窗口机制允许发送方在不等待确认的情况下连续发送多个数据包。
-
接收方会发送一个窗口大小的确认信息(rwnd),告诉发送方可以继续发送多少个数据包。
-
通过动态调整滑动窗口的大小,可以有效控制数据的发送速度。
-
-
接收方窗口调整:
-
接收方可以根据其接收缓存的大小,动态调整发送方的发送窗口大小。
-
如果接收方的处理能力较低,可以减小接收窗口的大小,从而限制发送方的发送速度。
-
-
基于时间的流量控制:
-
TCP 还可以使用基于时间的流量控制方法,例如持续计时器和超时机制。
-
发送方在发送数据之后会启动一个计时器,如果在一定时间内没有收到接收方的确认信息,发送方会认为数据丢失并重新发送。
-
通过持续计时器和超时机制,可以有效控制数据的发送速度。
-
滑动窗口机制
TCP 中的滑动窗口机制允许发送方在不等待确认的情况下连续发送多个数据包,主要包含以下部分:
-
窗口大小的确定:TCP 连接建立后,接收方会在确认应答报文中告知发送方自己的接收窗口(
rwnd
)大小(通过 TCP 首部中的 “窗口字段” 表示)。接收窗口大小代表接收方当前能够接收的数据量。例如,接收方告知发送方窗口大小为 500 字节,就意味着发送方最多可以发送 500 字节的数据给接收方。
-
数据发送与确认:发送方根据接收方的窗口大小,将数据分成多个报文段进行发送。每发送一个报文段,就会启动一个定时器等待接收方的确认应答。接收方成功接收数据后,会向发送方发送确认应答,其中包含确认号
ACK
和当前的接收窗口大小。确认号用于告知发送方下一个期望接收的字节序号,而接收窗口大小的更新则让发送方知道后续还可以发送多少数据。 -
窗口的滑动:随着接收方不断接收和处理数据,接收窗口会不断地向前滑动。比如,接收方最初的接收窗口范围是从序列号 1001 到 1500,当接收方成功接收并处理了序列号为 1001 到 1200 的数据后,接收窗口就会向前滑动,新的接收窗口范围变为从序列号 1201 到 1700(假设接收窗口大小没有变化)。发送方根据接收窗口的滑动情况,不断调整自己的发送窗口
swnd
,继续发送后续的数据。 -
流量控制的动态调整:如果接收方处理数据的速度较慢,导致接收窗口变小,接收方会在确认应答中告知发送方新的窗口大小。发送方收到后,就会相应地减少发送的数据量,以适应接收方的处理能力。
如果接收方处理速度较快,接收窗口变大,接收方也会通知发送方,发送方就可以增加发送的数据量,提高数据传输效率。
例如以下示例(DATA 表示这是 TCP 数据报文段):
-
初始状态:A 和 B 之间已经建立了TCP连接,A 开始给 B 发送数据。
-
A发送数据:
- A 的发送窗口(swnd)初始值为400,首先发送了一个包含
seq=1
到seq=100
的数据块。 - B 收到了这个数据块并返回了一个
ACK=1 ack=101
的确认消息,表示已经收到了seq=1
到seq=100
的数据。
- A 的发送窗口(swnd)初始值为400,首先发送了一个包含
-
继续发送数据:A 继续发送数据,发送了
seq=101
到seq=200
的数据块,每次发送的数据量不超过当前的发送窗口大小swnd
。 -
数据丢失:A 继续发送数据,发送了
seq=201
到seq=300
的数据块,在某个时刻,seq=201
到seq=300
的数据块丢失了。 -
B的第一次流量控制:由于 B 没有收到
seq=201
到seq=300
这部分数据,所以不会发送相应的确认消息,只返回了ACK=1 ack=201 rwnd=300
的确认消息,表示已经收到了seq=101
到seq=200
的数据。并告诉 A 它的接收窗口大小为rwnd=300
,即 B 最多可以再接收 300 个字节的数据。 -
A向前滑动窗口:A 根据 B 的接收窗口大小调整自己的发送窗口大小
swnd=300
,并向前滑动自己的窗口,新的接收窗口范围变为从序列号 201 到 500。A 继续向 B 发送seq=301
到seq=400
和seq=401
到seq=500
的数据块。 -
超时重传:A在一段时间后发现
seq=201
到seq=300
的数据块没有得到确认,于是重新发送这部分数据。 -
B的第二次流量控制:B 向 A 返回了
ACK=1 ack=501 rwnd=100
的确认消息,表示已经接收到seq = 500
之前的所有数据块,并告诉 A 它的接收窗口大小为rwnd=100
,即 B 最多可以再接收 100 个字节的数据。 -
A再次向前滑动窗口:A 根据 B 的接收窗口大小调整自己的发送窗口大小
swnd=100
,并向前滑动自己的窗口,新的接收窗口范围变为从序列号 501 到 600。A 继续向 B 发送seq=501
到seq=600
的数据块。 -
B的第三次流量控制:B 向 A 返回了
ACK=1 ack=601 rwnd=0
的确认消息,表示已经接收到seq = 600
之前的所有数据块,并告诉 A 它的接收窗口大小为rwnd=0
,即 B 不再接收任何数据。 -
A再次向前滑动窗口:A 根据 B 的接收窗口大小调整自己的发送窗口大小
swnd=0
,并向前滑动自己的窗口,但不再向 B 发送数据。
持续计时器
若发送方收到了接收方 rwnd=0
的确认消息后,会一直等待其发送非零窗口通知,即将窗口大小调整为大于0,若非零窗口丢失,则会导致发送方与接收方一直互相等待,造成死锁局面,将会一直持续下去。
为了打破由于非零窗口通知报文段丢失而引起的双方互相等待的死锁局面,TCP为每一个连接都设有一个持续计时器(Persistence Timer)。
TCP规定:即使接收窗口值为0,也必须接受零窗口探测报文段、确认报文段以及携带有紧急数据的报文段。
零窗口探测报文段也有重传计时器,当重传计时器超时后,零窗口探测报文段会被重传。
-
接收窗口变为0:主机 B 的接收窗口
rwnd
变为0,表明 B 暂时无法接收更多的数据。B 向 A发送一个ACK
报文,其中包含rwnd=0
的信息。 -
主机A暂停发送:A 收到主机 B 的
ACK
报文后,知道对方的接收窗口为0,因此将自己的swnd
调整为 0,暂停发送数据。 -
持续定时器启动:为了避免长时间的僵局,A 启动了一个持续定时器。当持续定时器到期时,即使 B 的接收窗口仍然为0,A 也会发送一个小的数据包(零窗口探测报文段,1字节)来试探 B 的状态。
-
试探数据包:B 收到零窗口探测报文段后,会返回一个新的
ACK
报文,其中包含了最新的接收窗口大小。 -
接收窗口恢复: 如果 B 的接收窗口已经变大,那么它会在 ACK 报文中包含新的
rwnd
值。A 收到这个 ACK 报文后,就可以恢复正常的数据发送,从而打破死锁局面。