文章目录
本篇要总结的是对于TCP的一些其他概念进行总结
流量控制
接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应
因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control)
对方缓冲区写满了
这里给出的是一个比较极端的情况,对方服务器对于报文的处理速度如果太慢的话,就会导致一瞬间对方的接收缓冲区就会被打满,那么此时如果继续进行发送就会出现丢包的情况出现,那么就会引起重传的现象,虽然有重传这样的机制存在,但是依旧不能随意使用这种策略,在进行重传的过程中会消耗额外的资源
因此TCP针对于这样的情况就设计出了流量控制的概念,TCP的首部会包含一个窗口大小的字段,那么依据这个窗口大小就可以设计出报文的大小,在进行一开始的三次握手的过程中,除了进行建立链接之外,同时也会进行这些诸如窗口大小这些信息的交互,这样就能知道对方的接受能力等等,所以说,我们对于TCP的三次握手来说,不应该只停留在建立链接的层面,事实上在进行三次握手的过程中,双方也进行了交换报文的操作,这个过程其实已经协商好了对应的双方交换能力
假设现在发送了一个报文,连续发了三个报文,那么对应的对方的接收缓冲区的窗口中如果变成0了,就说明此时已经被打满了,那么主机a就不会继续进行发送了,那么现在的问题是,主机a什么时候知道主机b把消息读走了呢?这个过程该如何进行理解呢?事实上,当主机a发现这个事之后,它不会在这坐以待毙,而是会定期的去发送一些用来窗口探测的报文,其原理就是主机b如果应答的时候会回复它对应的窗口大小,依据这个窗口大小就可以判断对方的主机缓冲区中是否有数据了
主机a发送失败
可是在网络中可能会出现各种各样的问题,比如在主机a等待的过程中,主机a和主机b的网络通信的信道出现问题了,此时主机a传输的信息就传输不到主机b当中,那么此时该如何处理呢?此时主机b为了自己的效率,它也有自己的策略,在自己的缓冲区被读走信息之后,那么主机b也会向主机a发送一个TCP的报头,用来通知主机a当前我主机b的缓冲区已经没东西了,可以继续给我发送消息了
基于这个原因就可以在一定程度上提高效率,同时,双方的相互通知是在网络中单向进行传输的,如果主机a那边出现问题,也不担心影响通信,主机b也会给主机a发送消息,这样就可以提高容错率的同时,还能保证窗口是处于更新的状态的
双方都失败
如果主机a给主机b发消息收不到回应,主机b也发消息收不到回应,那么在重传了很多次之后,双方的主机就都应该意识到是出现了差错,所以对应的解决措施就是会直接把异常链接进行关闭,代表出现了错误,不再进行传输了,这也就意味着当双方的通信不会再有任何可能的机会的时候,它们会选择直接把链接关闭,有自己主动关闭链接的能力,这是毫无疑问的结论
窗口扩大因子M
接收端如何把窗口大小告诉发送端呢? 回忆我们的TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息,那么问题来了, 16位数字最大表示65535, 那么TCP窗口最大就是65535字节么?实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位
总结
流量控制是属于效率还是可靠性呢?答案一定是属于可靠性,但是换个角度来讲,也正是因为有流量控制这样的因素存在,所以才能保证效率进行传输,流量控制主观上来讲是为了提高可靠性而产生的,但是也因为有这样的存在提高了效率,这两个概念也并不是完全冲突
滑动窗口
下面进入下一个话题,叫做滑动窗口的问题,在之前的认知中我们知道,TCP的传输是可靠的,这就意味着发出去的报文,在没有收到应答的时候会在我本地进行一个保存,如果出现丢包的现象,那么就必须要把这个数据包进行重新的拷贝和发送,那么这个过程该如何进行理解呢?
滑动窗口基本理解
客户端和服务器的地位是对等的,它们都有各自的发送缓冲区和接收缓冲区,那么假设对于客户端来讲,它要发送的数据此时存储在发送缓冲区当中,而发送的本质其实就是把数据拷贝给网卡,然后发送到网络就可以了,但是其实在发送了之后,对应的数据还是存储在缓冲区当中的,那么就意味着我们必须要对于这个缓冲区做出一个划分的操作,把它进行合适的划分:
如上所示就是一个简单的划分机制,对于这个发送缓冲区的理解,我们可以简单的把它理解为是一个数组,那么对于数组进行合适的分区,未来这个区域一旦划分好,那么发送的数据如果已经被确认了,那么就可以把它对应的信息进行覆盖了
在前面不管是文件系统还是磁盘的概念当中,对于计算机来说是没有删除和清空的概念的,只要认为它被删除其实就是认为这块区域可以被覆盖了,所以当有消息被确认被接受了之后,就意味着这块区域是可以被覆盖的,那么未来就会有新的元素把这块区域覆盖了即可
双指针
虽然到现在我们对于滑动窗口的理解还是停留在数组分区的概念,但是现在有的一个初步结论是滑动窗口就是这段缓冲区当中的一部分,那么缓冲区存在的区域是可以直接推送给接收方的,这也就意味着正是因为有滑动窗口到底存在,才支持着TCP可以允许同时把多个报文发送过去,而对于滑动窗口的理解目前可以停留在把它看成是一个数组,数组中存放的是一个一个的字节,所谓的已发送已确认,其实就是对于数组的下标确认,那这其实就是双指针的概念!
这点其实在前面的双指针的算法中了解过,所以这里就不过多介绍了,但是总的来说说白了滑动窗口就是俩指针,只不过是在发送的时候要把对应这个窗口里面的数据一块一块的发送过去即可
但是现在的问题是,既然已经设计好了滑动窗口这样的结构,那么为什么不直接把窗口当中的数据都发过去呢?这样又能提升效率,还不用担心其他问题?其实这是由硬件所决定的,至此对于窗口的边缘逻辑问题有了一个基本的认识
所以比方说有些报文被ACK了,比如传递过来信息有ACK2001,说明这个报文已经确认被收到了,那么此时滑动窗口就可以移动到对应的2001处,表示这前2000的内容的数据已经确认收到了
滑动窗口的范围
由此可以得出的一个结论来说是,原则上滑动窗口的范围越大,证明网络吞吐量就越高,对于滑动窗口来说它越大就证明可以同时向对方发送更多的数据,而前面也说了,滑动窗口不管怎么变,前提条件都必须是对方的接受范围,如果超过了接收范围,发送再多的数据也没有什么意义
所以我们目前认为它的大小是等于对方接收缓冲区的剩余大小的,后面会对这个概念进行一个更深入的理解
滑动窗口的滑动?
那么下面要讨论的问题是,滑动窗口能不能向左滑动呢?以及其他的滑动细节问题:
如果数据丢包了怎么办?
正常来说,TCP报文传输是这样的:
而这样的传输会导致性能比较低,所以又会有第二种发送方式,多个包一起发一起收
那有了上面图的概念,那我们该如何理解滑动窗口在遇到丢包后的解决方式呢?
1. 数据包递达,但是ACK丢了
这种情况下,其实是不重要的,这是因为后续的响应中还会包含对应的序号,这个序号代表的是在它之前的信息全部都被接受到了,所以其实是允许出现部分ACK丢失的,只要后面还有就问题不大
2. 数据包丢了
如上图所示,如果数据包丢了,那么此时就应该被重视了,数据包丢了之后就会一直传递的是1001的序号,而此时发送端也该意识到这一点,因此它就会去把数据再重新传递一次,而接收端在接受到对应的1001-2000的数据包之后,它就会直接把后面有的接上,直接传递7001
这样的机制就被叫做是高速重发控制,也叫做是快重传
快重传和超时重传
既然已经有了快重传,那为什么还要有超时重传呢?快重传确实可以提高效率,但是快重传不能保证在没有很多数据的时候还能触发这个机制,换句话说,超时重传是一种兜底的机制,如果快重传传了更好,如果它没有重传就需要超时重传进行兜底
重传的保底机制
因此我们说,滑动窗口的存在保证了不管是数据还是ACK应答,随便丢,只要丢了我都能找回来保证数据继续通信,这也是TCP传递是可靠的的一种保证
滑动窗口的越界问题
TCP的缓冲区本质上可以理解为是一个环状的结构,在进行处理的时候是不用担心数据越界的问题的,可以理解为是用数组模拟队列的环形队列的过程,把那个想法运用在对应的滑动窗口中即可
编号问题
最后我想说,TCP是面向连接的,这意味着双方在进行正常通信的时候必然要管理各自的链接,而实际在进行运用的时候会发现这个序号并不是从0开始的,这是因为传递了一个0-1000的报文,而这个报文在传输的过程中链接断开了,这个报文在传递到对方的缓冲区之前又建立链接了,没有完成数据消散的效果,如果还依旧传递0-1000的报文就可能会出现错误信息,基于这样的原因,双方在进行最开始的序号是随机的,随机的序号可以保证双方互相通信的随机性,避免出现上面的错误信息情况
延迟应答
如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小,所以针对这种情况就设计出了延迟应答的设计方案,多发送一个包再进行应答,这样可以保证窗口大小变大,同时保证数据的吞吐量变大