目录
一、关于滑动窗口在TCP中的应用
1.1什么是滑动窗口,为什么要有滑动窗口
1.2滑动窗口的实现
1.3滑动窗口针对丢包重传的处理机制
二、流量控制
一、关于滑动窗口在TCP中的应用
1.1什么是滑动窗口,为什么要有滑动窗口
在上一篇博文中博主阐述了确认应答ACK策略, 对每一个对方发送的数据段报文, 接收方都要给一个 ACK的确认应答。接收方收到 ACK应答后再发送下一个数据段报文。但是这样做有一个缺点, 那就是性能较差,效率较低,尤其是数据传输往返时间较长的时候。
这样一发一收的传输方式显然性能较低, 那么如果一次发送多条数据, 从而降低传输频率,这样一来就可以大大的提高数据报文传输的性能(其实也就是将多个数据段的等待时间重叠在一起。
而我们要传输的数据如果很大,那么在TCP层,其传输能力也是有限的,而且如果这时对方的接收缓冲区已经快满的话,不能存储过多数据,且对方应用层迟迟不去接收缓冲区读取数据的话,此时我们如果依旧发送大量的数据给对方,那么会有很大的概率出现丢包的情况,这时发送方就要根据传输协议的阈值以及对方接收能力来动态的调节每次发送的数据量。这时,维护一个窗口,通过滑动窗口来控制无疑就是最好的选择。
结合我们所介绍的多条数据一起发送(实际上是在没有接收ACK情况下连续的发送好几条报文),滑动窗口就可以一次确定一段发送区间,然后分段进行发送,得到对方应答确定报文被对方接收后继续向后移动去发送还未被发送的报文。
1.2滑动窗口的实现
• 窗口的大小指的是无需等待确认应答而可以继续多次发送数据的最大值. 上图中的窗口大小就是4000个字节(四个段)。
• 刚开始发送前四个段的时候, 不需要等待任何的ACK应答,直接连续发送报文;
• 当收到第一个 ACK应答之后, 滑动窗口就继续向后移动, 继续发送第五个段的数据;依次往后类推;
• 操作系统内核为了维护这个滑动窗口, 需要开辟一个发送缓冲区来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉;
• 窗口越大, 则说明网络的吞吐率就越高;
•滑动窗口也采用类似环状结构,每次通过取模类似的操作来将指针始终控制在存放数据的区间,当将后面的数据全部发送完毕后,滑动窗口会自动回绕会区间头部去处理并发送新的数据。
所以在网络较好情况下,滑动窗口的大小一般是对方接收缓冲区中剩余空间的大小。
1.3滑动窗口针对丢包重传的处理机制
根据如上描述,如果真的因为特殊原因出现了丢包, 那滑动窗口该如何进行重传? 这里分以下两种情况:
情况一: 数据包已经抵达, ACK应答丢包了
在双方都遵循TCP协议的情况下,如果情况一出现,主机B接收到了主机A的所有报文并且对所有报文都进行了ACK应答,但是应答在传输过程中丢了,这时起始问题并不大,网络正常情况下,也不会让主机A等到超时重传,因为主机B已经实实在在接收到了主机A的报文,所以确认序号是正常往后走的,这里需要重申确认序号的功能:确认序号之前的所有报文都已被接收。所以只要后面发送给主机A的ACK应答能传达到,那么主机A也可以知道在此之前的报文对方都接收到了,就不会再进行超时重传了。
比如上图所示,就算前几个应答都丢失了,但只要后面6001的应答传给了主机A,那么主机A就知道,虽然前面的应答我没有接收到但是对方的6001说明6001之前全部到达了,那只是ACK丢包了而已,而ACK本身就是为了确认数据可靠性的,本身并没有携带什么有价值的信息,所以主机A就不会再管6001之前的报文了。
情况二:数据包直接丢了
这种情况下,数据包直接丢了,是实实在在的丢包,那么没有ACK,主机A也不确定到底主机B是收到了还是没收到,此时TCP协议就起了作用。
•当某一段报文段丢失之后,比如1001-2000的数据包丢了,那么发送端会一直收到1001的 ACK, 因为发送的每条报文都有自己的序号,而接收方会对接收的报文进行排序,然后按序进行处理,如果对应序号的报文丢失,那么接收方会一直重复发送该报文之前的确认序号,以此达到提醒对方丢包,就像是在提醒 发送端 "我想要的是 1001" 一样;
• 如果发送端主机连续三次收到了同样一个 "1001" 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;
• 这个时候当接收端收到了序号1001的报文之后, 再次返回的 ACK应答就是7001了(因为 2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中;
而这种机制也被称为 "高速重发控制"(也叫 "快重传"机制)。
二、流量控制
在上面博主提到过网络资源和传输效率是有限的。
接收端处理数据的速度也是有限的。如果发送方发送端发的太快, 导致接收端的缓冲区被存储满, 这 时候如果发送端继续发送报文, 就会造成丢包, 从而引起丢包重传等等一系列连锁反应降低了网络传输的效率。因此 TCP 支持根据接收端的处理能力及接收缓冲区大小, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control);
• 接收端将自己可以接收的缓冲区大小(也就是接收缓冲区中剩余的大小)放入TCP 首部中的 "窗口大小" 字段, 在发送ACK应答时携带通知发送端;
• 窗口大小的字段越大, 说明网络的吞吐量就越高;
• 当接收端一旦发现自己的缓冲区快满了时, 就会将窗口大小字段设置成一个更小的值通知给发送端;
• 发送端接受到这个窗口之后, 就会减慢自己的发送速度;
• 如果接收端缓冲区一旦满了, 就会将窗口大小置为 0; 这时发送方就不再发送数据, 但是需要定期发送一个窗口探测数据段, 时刻得知对方缓冲区是否有剩余空间,使接收端把窗口大小告诉发送端。
那么问题来了, 16位二进制位所存储的数字最大表示65535, 那么 TCP 窗口最大就是 65535 字节么? 实际上, TCP 首部 40 字节选项中还包含了一个窗口扩大因子 M, 实际窗口大小是窗口字段的值左移了M 位;我们可以根据16位中存储的值再左移M得出实际的大小。