在前面关于可靠传输的内容里面,提到了发送方的“发送窗口”。发送窗口之外的数据不会被发送,通过调整发送窗口的大小,就可以控制发送方的发送速度。不过,我们前面讨论TCP的可靠传输时,为了简化说明,把发送窗口的大小固定。接下来讨论发送窗口大小的调整的问题。
调整发送窗口的大小是TCP的精髓,无论是流量控制还是在拥塞控制,都是通过恰当的调整发送窗口的大小来实现的。本节先讨论流量控制。流量控制的核心思想就是:发送方按照接收方的接收能力来调整发送窗口的大小。
那么,接收方的接收能力是如何体现的呢?在前面“面向字节流”部分里就提到过缓存。只要发送方有发送缓存,接收方就有接收缓存。当然,TCP是全双工协议,通信的双方每方都各有一个发送缓存和一个接收缓存。为了讨论问题方便,此处仅考虑一个方向的数据传输。对于接收方而言,其接收能力的大小,最直接的体现就是其接收缓存的可用空间大小。图5-13所示为接收缓存和发送缓存的状况。
图5-13 接收缓存和发送缓存的状况
在发送方,只要发送缓存还有空间,应用程序就可以向发送缓存内写入数据。这些数据会逐渐地被纳入发送窗口,渐次发送。在收到确认信息后,数据离开发送窗口,其占用的缓存空间就可以被释放,允许应用程序写入新的数据。
在接收方,被正确接收的数据放置在接收缓存之中,这些数据将提供给应用程序使用。数据被应用程序取走后,其占据的接收缓存就会被再次空闲出来。接收方的接收能力就体现在接收缓存可用空闲空间的大小。接收方利用协议提供的机制通知发送方:“我的缓存空间还有多少字节空余,你发送的数据不要超过这个值,不然我就无法接收。”只要发送方发送的数据不超过这个限度,便可以保证其发出的数据不会因为接收方的接收能力不足而被丢弃。这便是流量控制最基本的内容。
在实际协议处理中,为了方便管理,在接收方设置了接收窗口。在没有乱序到达的情况下,接收窗口的大小肯定不会超越可用空闲缓存的大小。打个接收货物的比方,对于接收方来说,接收缓存是它仓库空间的大小,接收窗口则表示它计划接收货物数量的多少。它的计划不会超越仓库剩余的容量,但在仓库容量允许的情况下,计划接收多少货物完全由它自己做主,这样处理更为灵活。这样一来,接收方通过协议机制通知发送方的并不是“我的缓存空间还有多少字节空余”,而是“我计划再接收多少字节的数据”。当然,接收计划经常直接按剩余空间大小来制定。
和发送窗口类似,接收窗口也设置有起始位置和终止位置。接收窗口的起始和终止位置也是用字节的序号标识的,只有落在这个序号范围内的字节才会被接收方接收,至于范围外的数据,接收方都将不予理睬。
接收窗口的大小是由接收方的协议处理程序根据自身情况自行决定的。这种相当于接收计划的接收窗口信息,是通过TCP数据段首部的“确认号”和“窗口”两个字段传递给发送方的。“确认号”相当于指示了接收窗口起始位置,“窗口”字段指示了窗口的大小。通过这两个字段自然可以计算出窗口结束的位置。TCP处理程序在每次接收到对方的数据段时,都要依据其首部的“确认号”和“窗口”两个字段调整自己的发送窗口,即发送窗口是按照接收窗口的样子来调整的。发送方按照接收方的接收计划来制订自己的发送计划,这样就保证了不会发送超越接收方能力的数据量。
当然,网络传递是有时延的,虽然从效果上来看,发送窗口按照接收窗口来调整自己的大小和位置,但这种调整总会有一定的滞后性,不过,依然可以保证发送方的发送量不会超越接收方的接收能力。对于延迟到达的确认包,按照协议规定是不予理睬的。所以,不会按照其“确认号”+“窗口”的组合而把发送窗口向左滑动。(www.xing528.com)
由于TCP数据段首部的窗口字段只有两个字节,接收窗口最大只能达到64 KB,这意味着,发送窗口最大也只有64 KB。互联网从刚刚投入民用到发展至今,一般用户的接入速度至少提高了五个数量级左右。如此小的窗口已经无法满足现代网络的传输需求。TCP首部的可选项中有窗口扩大选项,通过它可以用30 bit来表示窗口大小,让窗口达到1Gb的大小,迎合网络发展的需要。
下面举例说明流量控制的过程。这个例子还是只考虑单向的数据发送,接收窗口则尽力使用全部的缓存剩余空间。假设接收方的接收缓存是400 Byte。当建立TCP连接时,确定了发送方的起始字节号(在这个例子中还是使用1)和接收方MSS(还是使用100 Byte)。接收方还在建立连接的报文中把整个接收缓存的字节数填入首部的窗口字段,并传递给发送方。流量控制过程示意如图5-14所示。
从图5-14所示的流量控制过程中可以看到,接收窗口随着接收到数据而缩小,随着数据被应用程序读取而扩大。发送方在获得接收确认后,将自己的发送窗口按照接收窗口进行调整。图5-14代表双方一个交互过程中涉及操作的每个框中,带有下划线的一行为发送操作,在该操作发送的数据被对方接收后,对方会据此调整改变自己的窗口。
图5-14 流量控制过程示意
在这个例子模拟的通信场景中,发送方数据发送的速度要超过接收方读取的速度,但通过流量控制限制了发送方的发送速率,保证接收方不会因为缓存溢出而丢失数据。最终,接收窗口和发送窗口的大小都变为0。接收方没有可用的缓存,发送方也不能再发送数据,这种状态需要接收方程序将缓存内的数据取走,空出缓存空间以后才可能改变。
当接收方的缓存重新出现空闲,可以再次接收数据的时候,需要接收方向发送方发送数据段。依靠该数据段的ACK和RWND的取值,发送方获得接收窗口信息,调整发送窗口,继续通信任务。
但是,接收方用以打破僵局,说“我这边又有地方了,计划再接收多少货物”的这个数据段也存在丢失的可能性。如此一来,双方的通信便会陷入一种死锁状态。为了避免这种事件的发生,在发送方因为发送窗口归零而无法发送数据的情况下,会以诸如几十秒这样相对较长的时间间隔,向接收方发送只包含1 Byte数据的窗口试探报文,引导对方发回确认数据段,通过这个回应中知悉对方接收窗口的大小。若接收窗口已经大于0,则会收到对该字节的确认和窗口新信息。若接收窗口仍然为0,则对此种窗口之外的数据拒绝接收,并对以前的数据重复确认。发送这种窗口试探报文,正如商家拿着样品,对一段时间不再订货的客户进行上门咨询。
通过流量控制,TCP实现了让发送程序的发送速率去主动匹配接收程序的接收能力的效果,避免了由于接收方无能力接收而导致的数据丢失。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。