- 虚线矩形框,就是发送窗口。
- SND.WND:表示发送窗口的大小,上图虚线框的格子数是 14 个,即发送窗口大小是 14。
- SND.NXT:下一个发送的位置,它指向未发送但可以发送的第一个字节的序列号。
- SND.UNA:一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号。
接收方的滑动窗口包含三个部分:
- 已成功接收并确认
- 未收到数据但可以接收
- 未收到数据且不可以接收的数据
- 虚线矩形框,就是接收窗口。
- REV.WND:表示接收窗口的大小,上图虚线框的格子数就是 9 个,即接收窗口的大小是 9。
- REV.NXT:下一个接收的位置,它指向未收到但可以接收的第一个字节的序列号。
什么是拥塞控制?不是有了流量控制吗?
前⾯的流量控制是避免发送⽅的数据填满接收⽅的缓存,但是并不知道整个⽹络中发⽣了什么。
⼀般来说,计算机⽹络都处在⼀个共享的环境。因此也有可能会因为其他主机之间的通信使得⽹络出现拥堵。
在⽹络出现拥堵时,如果继续发送⼤量数据包,可能会导致数据包延迟、丢失等,这时 TCP 就会重传数据,但是⼀重传就会导致⽹络的负担更重,于是会导致更⼤的延迟以及更多的丢包,这个情况就会进⼊恶性循环并且被不断地放⼤…
所以,TCP 不能忽略整个网络中发⽣的事,它被设计成⼀个⽆私的协议,当⽹络发送拥塞时,TCP 会⾃我牺牲,降低发送的数据流。
于是,就有了拥塞控制,拥塞控制的⽬的就是为了避免发送⽅的数据填满整个⽹络。
就像是一个水管,不能让太多的水(数据流)流入水管,如果超过水管的承受能力,水管就会被撑爆(丢包)。
发送方维护一个拥塞窗口 cwnd(congestion window) 的变量,调节所要发送数据的量。
什么是拥塞窗⼝?和发送窗⼝有什么关系呢?
拥塞窗⼝ **cwnd **是发送⽅维护的⼀个状态变量,它会根据⽹络的拥塞程度动态变化。
发送窗⼝ swnd 和接收窗⼝ rwnd 是约等于的关系,那么由于加⼊了拥塞窗⼝的概念后,此时发送窗⼝的值 swnd = min(cwnd, rwnd),也就是取拥塞窗⼝和接收窗⼝中的最⼩值。
拥塞窗⼝ cwnd 变化的规则:
- 只要⽹络中没有出现拥塞, cwnd 就会增⼤
- 但如果⽹络中出现了拥塞, cwnd 就会减小
拥塞控制有哪些常用算法?
慢启动
慢启动算法,慢慢启动。
它表示 TCP 建立连接完成后,一开始不要发送大量的数据,而是先探测一下网络的拥塞程度。由小到大逐渐增加拥塞窗口的大小,如果没有出现丢包,每收到一个 ACK,就将拥塞窗口 cwnd 的大小加 1(单位是 MSS)。每轮次发送窗口增加一倍,呈指数增长,如果出现丢包,拥塞窗口就减半,进入拥塞避免阶段。
举个例子:
- 连接建⽴完成后,⼀开始初始化 cwnd = 1 ,表示可以传⼀个 MSS ⼤⼩的数据。
- 当收到⼀个 ACK 确认应答后,cwnd 增加 1,于是⼀次性能够发送 2 个。
- 当收到 2 个 ACK 确认应答后, cwnd 增加 2,于是就能⽐之前多发送 2 个,所以这⼀次能够发送 4 个。
- 当这 4 个 ACK 确认到来的时候,每个确认 cwnd 增加 1, 4 个确认 cwnd 增加 4,于是就能⽐之前多发送 4 个,所以这⼀次能够发送 8 个。
发送包的个数是呈指数性增⻓的。
为了防止 cwnd 增长过大而引起网络拥塞,还需设置一个慢启动阀值 ssthresh(slow start threshold)的状态变量。当 cwnd 到达该阀值后,就好像水管被关小了水龙头一样,减少了拥塞状态。即当 cwnd > ssthresh 时,进入拥塞避免算法。
拥塞避免
一般来说,慢启动阀值 ssthresh 的大小是 65535 字节,cwnd 到达慢启动阀值后
- 每收到一个 ACK 时,cwnd = cwnd 1/cwnd
- 当每过一个 RTT 时,cwnd = cwnd 1
显然这是一个线性上升的算法,可以避免发送过快导致网络出现拥塞问题。
接着上面慢启动的例子,假定 ssthresh 为 8:
- 当 8 个 ACK 确认应答到来时,每个确认增加 1/8,8 个 ACK 确认后 cwnd ⼀共增加 1,于是这⼀次能够发送 9 个 MSS ⼤⼩的数据,变成了线性增⻓。