TCP协议是一种面向可靠消息传输的协议,它对连接的两端传输的数据做了严格的追踪,以便在必要时做重传和排序。为了达到这种追踪效果,发送方和接收方都设计有缓存。TCP的滑动窗口属于缓存的一部分,接收方向发送方声明自己一次性可以接收多少数据,发送方发送的数不能超过接收方的窗口大小;如果接收方窗口满了,则接收方会将自己的窗口为0的信息告诉发送方,发送方就会停止发送数据,直到接收方有空间继续接收数据。
那么滑动窗口到底可以有多大?我们回答这个问题前,首先观察下TCP的协议头部。
TCP的协议头部,接收窗口字段的长度为16bit,16bit最大可以表示的无符号整数为65535,所以TCP的滑动窗口最大为65535字节吗?
但是我们看下面这个在真实服务器上的抓包结果,有很多接收窗口超过65535的数据包(比如Win=87168、116096等,表示接收窗口大小为87168、116096字节),这些抓包数据说明问题绝对不会这么简单,那TCP协议是如何做到滑动窗口尺寸超过65535字节的呢?(图是服务器上截的,不太美观,不过不影响问题解释)
缩放因子概述
我们设想一种高带宽高延迟的网络场景,假设一个10Mbps带宽的网络链路上,TCP通信两端网络单向100ms时延,且TCP连接的两端接收窗口都设置为65535。我们可以计算一端向另外一端在某时刻最大传输量10000000bit / 8 * 0.1 = 12500Byte,即一端A收到另外一端B发送的数据第一个字节时,B最大可以向网络写入12500Byte,如果滑动窗口只有65535字节,意味着我们并不能一次性传输12500字节,这就造成了网络带宽的浪费。
TCP (Window Scaling Factor)缩放因子在RFC1072提出,并且在RFC 1323细化,后来又被RFC7323替代,它被用来扩展原先有限的TCP接收窗口。但是直接扩展TCP协议头的16bit窗口字段的位数是不可接受的,这会导致新老协议不兼容,因此工程师们使用了TCP头部的扩展选项设置一个count值,TCP的滑动窗口大小就是16bit的原始滑动窗口左移count位。假如count值为3,则左移3位就是8。
举个例子,如果原始窗口大小为1460Byte,缩放因子的count值为3,那么最终的滑动窗口大小就是1460 * (2^3)=1460*8=11680字节。
抓包观察
下面我们抓包观察下TCP的缩放因子,下图是一次TCP连接的完整数据包流程(建立连接->传输数据->断开连接)
然后我们打开第一个SYN包,option字段里,确实有携带Window Scale因子,数值为9(左移9位后实际值为512)
第二个SYN+ACK的包里,在option字段里同样携带有Window Scale因子,数值为9(左移9位后实际值为512)
然后观察后续的数据包,则不再包含Window Scale因子
缩放因子设置
缩放因子这个字段在TCP三次握手阶段由双方的SYN报文携带,用于告知对方自己真实的接收窗口大小。为了新老协议兼容,只有连接两端都携带缩放因子时此option才生效。
缩放因子最大可以设置位14,为什么是这个数据,RFC里有解释过这么设计的考虑:原始TCP窗口最大为65535字节,在左移14位后,这个大小为65535* (2^14)=1073725440字节,这已经有1G Byte的大小,在真实网络环境下已经完全可以应付高带宽高延迟的场景。为了避免过度扩展和大滑动窗口导致的诸如内存消耗,拥塞控制的问题,缩放因子的最大值被确定为14。
问题解答
最后我们回答一下标题里的问题:TCP的滑动窗口最大尺寸不仅仅和原始16bit的窗口字段有关,也和窗口缩放因子有关,原始滑动窗口最大可以表示65535字节,窗口缩放因子最大为14(左移14位后实际值为16384),因此滑动窗口理论最大尺寸为65535*16384=1073725440字节,这个尺寸可以满足当前网络条件下绝大多数高带宽高延迟场景下的数据传输
原创文章,作者:速盾高防cdn,如若转载,请注明出处:https://www.sudun.com/ask/76049.html