为了更有效地进行通信,TCP 协议在数据进行数据传输时,使用滑动窗口机制来同时发送多个数据包。当数据包丢失时,TCP 协议利用数据重发功能重新发送数据包。因接收端接收数据包的能力不同,TCP 流控制会根据接收端的能力发送适当数量的数据包。
数据分片
数据从主机传送到另一个主机往往要经过路由器、网关等设备。这些设备都要对经过的数据进行处理。由于这些设备处理数据有一定的限制,不能处理超过额定字节的数据,所以发送的时候需要确定发送数据包的最大字节数。这个最大字节数被称为最大消息长度(Maximum Segment Size,MSS)。当要发送的数据超过该值,就需要将数据分为多个包,依次发送。该操作被称为数据分片。
MSS 是 TCP 数据包每次能够传输的最 大数据量。通常,最大值为 1460 字节。如果发送的数据包大小大于 MSS 值,数据包将会被分片传输。分片原理如图所示。
滑动窗口机制
在进行数据传输时,如果传输的数据比较大,就需要拆分为多个数据包进行发送。TCP 协议需要对数据进行确认后,才可以发送下一个数据包,如图所示。例如,窗口大小为 3,数据包的传输如图所示。
以这种方式发送,就可以省去多个数据包(第1、2、4、5、7、8个)的确认应答包时间,从而避免了网络的吞吐量的降低。
窗口大小指的是可以发送数据包的最大数量。在实际使用中,它可以分为两部分。第一部分表示数据包已经发送,但未得到确认应答包;第二部分表示允许发送,但未发送的数据包。在进行数据包发送时,当发送了最大数量的数据包(窗口大小数据包),有时不会同时收到这些数据包的确认应答包,而是收到部分确认应答包。
那么,此时窗口就通过滑动的方式,向后移动,确保下一次发送仍然可以发送窗口大小的数据包。这样的发送方式被称为滑动窗口机制。设置窗口大小为 3,滑动窗口机制原理如图所示。
由于窗口大小为 3,发送端除了最后一个包以外,还可以继续发送下两个数据包(5001-6000 和 6001-7000)。窗口滑动到 7001 处。
数据重发
在进行数据包传输时,难免会出现数据丢失情况。这种情况一般分为两种。- 第一种,如果未使用滑动窗口机制,发送的数据包没有收到确认应答包,那么数据都会被重发;如果使用了滑动窗口机制,即使确认应答包丢失,也不会导致数据包重发。
- 第二种,发送的数据包丢失,将导致数据包重发。
下面详细介绍使用滑动窗口机制的两种情况。
确认应答包丢失
这种情况指的是前面发送的数据包没有收到对应的确认应答。当收到后面数据包的确认应答包,表示前面的数据包已经成功被接收端接收了,发送端不需要重新发送前面的数据包了。如图所示。1) 发送端第 1 次发送数据包:这里设置的窗口大小为 3,可以最大发送 3 个数据包。发送端同时发送 3 个数据包 1-1000、1001-2000 和 2001-3000。
2) 接收端返回确认应答包:接收端接收到这些数据,并给出确认应答包。数据包 1-1000 和数据包 2001-3000 的确认应答包没有丢失,但是数据包 1001-2000 的确认应答包丢失了。
3) 发送端第 2 次发送数据包:发送端收到接收端发来的确认应答包,虽然没有收到数据包 1001-2000 的确认应答包,但是收到了数据包 2001-3000 的确认应答包。判断第一次发送的 3 个数据包都成功到达了接收端。再次发送 3 个数据包 3001-4000、4001-5000 和 5001-6000。
4) 接收端返回确认应答包:接收端接收到这些数据,并给出确认应答包。数据包 3001-4000 和数据包 4001-5000 的确认应答包丢失了,但是数据包 5001-6000 没有丢失。
5) 发送端第 3 次发送数据包:发送端收到接收端发来的确认应答包,查看到数据包 5001-6000 收到了确认应答包。判断第 2 次发送的 3 个数据包都成功到达了接收端。再次发送 3 个数据包 6001-7000、7001-8000 和 8001-9000。
发送数据包丢失
这种情况指的是发送端发送的部分数据包没有达到接收端。那么,如果在接收端收到的数据包,不是本应该要接收的数据包,那么就会给发送端返回消息,告诉发送端自己应该接收的数据包。如果发送端连续收到 3 次这样的数据包,就认为该数据包成功发送到接收端,这时就开始重发该数据包。如图所示。
1) 发送端发送数据包:这里窗口大小为 4,发送端发送 4 个数据包,分别为 1-1000、1001-2000、2001-3000 和 3001-4000。
2) 接收端返回确认应答包:接收端接收到这些数据,并给出确认应答包。接收端收到了数据包 1-1000,返回了确认应答包;收到了数据包 1001-2000,返回了确认应答包;但是数据包 2001-3000,在发送过程中丢失了,没有成功到达接收端。数据包 3001-4000 没有丢失,成功到达了接收端,但是该数据包不是接收端应该接收的数据包,数据包 2001-3000 才是真正应该接收的数据包。因此收到数据包 3001-4000 以后,接收端第一次返回下一个应该发送 2001 的数据包的确认应答包。
3) 发送端发送数据包:发送端仍然继续向接收端发送 4 个数据包,分别为 4001-5000、5001-6000、6001-7000 和 7001-8000。
4) 接收端返回确认应答包:接收端接收到这些数据,并给出确认应答包。当接收端收到数据包 4001-5000 时,发现不是自己应该接收的数据包 2001-3000,第二次返回下一个应该发送 2001 的数据包的确认应答包。当接收端收到数据包 5001-6000 时,仍然发现不是自己应该接收的数据包 2001-3000,第三次返回下一个应该发送 2001 的数据包的确认应答包。以此类推直到接收完所有数据包,接收端都返回下一个应该发送 2001 的数据包的确认应答包。
5) 发送端重发数据包:发送端连续 3 次收到接收端发来的下一个应该发送 2001 的数据包的确认应答包,认为数据包 2001-3000 丢失了,就进行重发该数据包。
6) 接收端收到重发数据包:接收端收到重发数据包以后,查看这次是自己应该接收的数据包 2001-3000,并返回确认应答包,告诉发送端,下一个该接收 8001 的数据包了。
7) 发送端发送数据包:发送端收到确认应答包后,继续发送窗口大小为 4 的数据包,分别为 8001-9000、9001-10000、10001-11000 和 11001-12000。
TCP 流控制
在使用滑动窗口机制进行数据传输时,发送方根据实际情况发送数据包,接收端接收数据包。但是,接收端处理数据包的能力是不同的。1) 如果窗口过小,发送端发送少量的数据包,接收端很快就处理了,并且还能处理更多的数据包。这样,当传输比较大的数据时需要不停地等待发送方,造成很大的延迟。
2) 如果窗口过大,发送端发送大量的数据包,而接收端处理不了这么多的数据包,这样,就会堵塞链路。如果丢弃这些本应该接收的数据包,又会触发重发机制。
3) 为了避免这种现象的发生,TCP 提供了流控制。所谓的流控制就是使用不同的窗口大小发送数据包。发送端第一次以窗口大小(该窗口大小是根据链路带宽的大小来决定的)发送数据包,接收端接收这些数据包,并返回确认应答包,告诉发送端自己下次希望收到的数据包是多少(新的窗口大小),发送端收到确认应答包以后,将以该窗口大小进行发送数据包。
TCP 流控制过程如图所示。
第一部分
发送端根据当前链路带宽大小决定发送数据包的窗口大小。这里,窗口大小为 3,表示可以发送 3 个数据包。因此发送端发送了 3 个数据包,分别为 1-1000、1001-2000 和 2001-3000。接收端接收这些数据包,但是只能处理 2 个数据包,第 3 个数据包 2001-3000 没有被处理。因此返回确认应答包,设置窗口大小为 2,告诉发送端自己现在只能处理 2 个数据包,下一次请发送 2 个数据包。
第二部分
发送端接收到确认应答包,查看到接收端返回窗口大小为 2,知道接收端只处理了 2 个数据包。发过去的第 3 个数据包 2001-3000 没有被处理。这说明此时接收端只能处理 2 个数据包,第 3 个数据包还需要重新发送。因此发送端发送 2 个数据包 2001-3000 和 3001-4000。接收端收到这两个数据包并进行了处理。此时,还是只能处理 2 个窗口,继续向发送端发送确认应答包,设置窗口为 2,告诉发送端,下一个应该接收 4001 的数据包。
第三部分
发送端接收到确认应答包,查看到接收端返回窗口大小为 2。说明接收端接收了上次发送的 2 个数据包。此时仍然可以处理 2 个数据包,继续发送数据包 4001-5000 和 5001-6000。如果在接收端返回的确认应答包中,窗口设置为 0,则表示现在不能接收任何数据。这时,发送端将不会再发送数据包,只有等待接收端发送窗口更新通知才可以继续发送数据包。
如果这个更新通知在传输中丢失了,那么就可能导致无法继续通信。为了避免这样的情况发生,发送端会时不时地发送窗口探测包,该包仅有1个字节,用来获取最新的窗口大小的信息。
原理如图所示。
1) 发送端发送数据。发送端以窗口大小为 2,发送了 2 个数据包,分别为 4001-5000 和 5001-6000。接收端接收到这些数据以后,缓冲区满了,无法再处理数据,于是向发送端返回确认应答包,告诉它下一个接收 6001 的数据,但是现在处理不了数据,先暂停发送数据,设置窗口大小为 0。
2) 发送端暂停发送数据。发送端收到确认应答包,查看到下一次发送的是 6001 的数据,但窗口大小为 0,得知接收端此时无法处理数据。此时,不进行发送数据,进入等待状态。
3) 接收端发送窗口大小更新包。当接收端处理完发送端之前发来的数据包以后,将会给发送端发送一个窗口大小更新包,告诉它,此时可以发送的数据包的数量。这里设置窗口大小为 3,表示此时可以处理 3 个数据包,但是该数据包丢失了,没有发送到发送端。
4) 发送端发送窗口探测包。由于窗口大小更新包丢失,发送端的等待时间超过了重发超时时间。此时,发送端向接收端发送一个窗口探测包,大小为 1 字节,这里是 6001。
5) 接收端再次发送窗口大小更新包。接收端收到发送端发来的探测包,再次发送窗口大小更新包,窗口大小为 3。
6) 发送端发送数据。发送端接收到窗口大小更新包,查看到应该发的是 6001 的数据包,窗口大小为 3,可以发送 3 个数据包。因此发送了数据包,分别为 6001-7000、7001-8000 和 8001-9000。