这三个控制在tcp中是重要的控制程序,这些控制是为了让我们的TCP协议在网络传输中变得可靠与高效。
这三个控制其实是相辅相成的,也是高度解耦的控制模块。
细分的话,我们的滑动窗口与流量控制属于终端的控制,而拥塞控制,是为了防止为了拥塞。
如果主机与主机的交流是发送完报文就必须等待ACK应答才可再发送报文,这是不合理的,会将是极大的浪费资源的表现。
所以我们需要同时发送多次报文给对方,而且需要保证数据丢包后必须保证在对应的确认应答前,必须保护数据在一段数据不会被修改覆盖,保证需要重发时能够找到并重新发送。
现在看图是不是怪怪的,发送确认序号:1001,却发送了4001~5000,奇怪吧?
这时候就出现了滑动窗口的概念。
滑动窗口是可以接收1001确认序号而不影响发送问题的。
我们先再一次介绍一下序号和确认序号。
我们将发送缓冲区的每个字节编号,将对应的数据发送
然后我们包一大个报文数据看成一个一个段。而我们的滑动窗口就是一些连续的段。
- //发送缓冲区伪代码
- struct 滑动窗口
- {
- sendbuff[NUM];//发送缓冲区
- int win_start;//滑动窗口头
- int win_end; //滑动窗口尾
- //....
- }
win_start与win_end,是两个整型(也可以说是地址下标),我们把整个发送方的发送缓冲区中定义一块区域为滑动窗口,这是一个灵活的窗口,他的大小由win_start到win_end中间包含的范围被我们称之为滑动窗口,滑动窗口的数据可以同时全部发送,不必等待应答。
滑动窗口的大小并非固定,每次接收到确认应答后,我们的滑动窗口就会每次调整(除非是窗口探测报文这属于流量控制后面讲),win_start根据应答报文的确认序号更新,然后win_end=win_start+接收端的接收能力(这个说法有点问题);
滑动窗口调整的情况有许多,我们举几个例子证明滑动窗口并不是意味的向右固定大小
正常移动:
接收到了2001~3000的应答报文3001、16为窗口大小:3000。
窗口减小:
接收到了3001~4000的应答报文4001、16为窗口大小:2000
窗口大小为0:
接收到了5001~6000的应答报文6001、窗口大小为0。
这里阐述了2个知识
注意,虽然滑动窗口为0,无法再次发送携带数据的报文,但是会发送探测窗口报文(探测对方接收能力的报文),对应探测报文的确认应答返回,我们就知道了对端现在是否有剩余的空间允许我发送报文。当然接收方一旦倒腾出剩余也会发送窗口更新报文,通知我可以发送带数据报文了。
我们知道当一个报文长时间未得到应答,就会发起超时重传。
这个时间间隔并不是固定的,这个不能太长也不能太短否则会影响效率或者数据紊乱,一般而言是以网络为标准的,但是我们并不知道网络是否通畅,所以等待超时重发的时间不能是固定的。Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时 时间都是500ms的整数倍。
也就是说:
为什么说我们的滑动窗口有所帮助呢?因为在未收到该报文应答或者之后报文的应答的时候,滑动窗口会保护该报文,在用户层写入数据是不允许覆盖滑动窗口的数据,只会覆盖的到应答的报文。滑。
用户成写入代码只会写入空段和被应答了的报文位置,在滑动窗口内的报文都属于可能会重发数据,需要妥善保护。
配合对端快速发现丢包情况,不必等待超时重传机制,直接再次发送丢包报文,这也是一种滑动窗口高效的行为,作为为TCP协议在可靠中并且提升了效率。
client同时发送多条报文,1~1000、1001~2000、2001~3000、3001~4000、4001~5000。
server端在接收的报文的同时也会检查结束到的报文序号是否与实际大小连贯,一旦发现某一报文跨度大,立刻发生缺失部分的报文数据,在发生方多次收到之前报文的ACK应答(一般3次)然后就会得知,我的ACK应答后的第一个报文丢了,我需要重新发送这个报文了。
那么有这么牛的机制,还需要超时重复吗?需要,这是相辅相成的机制。
在这里说明:如果是频繁的TCP协议交流的情况下,快重发机制优先于超时重传机制,但是如果是低交流状态下,我们需要超时重传做为我们最后的可靠机制。