一、前言
说道TCP滑动窗口协议,相信大家都很熟悉,但是说道 Window Scaling参数或许知道的和用过的人却不多,本文我们来谈谈Window Scaling的由来
二、TCP滑动窗口
众所周知,TCP是一种面向连接可靠消息传输协议;为了保证可靠,连接的两端保持对所有传输数据的严格跟踪,以便在需要时候进行重传或重新排序。另外为了跟踪已经发送了的数据在发送端有TCP发送缓存,在接受端有接受缓存,滑动窗口则是这个缓存的一部分,接收方接受数据后会把ack和当前滑动窗口可用空间告诉发送方,发送方则发送的数据不能超过接收方剩余窗口大小,如果接收方窗口内数据还没来得及由应用程序读取,窗口满了,则发送方会停止发送数据,直到接收方滑动窗口有空间。
假设我们有两个主机A和B,它们建立了一个TCP连接。在连接开始时(握手时候协商),两个主机为传入数据分配32 KB的缓冲区空间,因此每个主机的初始窗口大小为32,768。
主机A需要向主机B发送数据,一开始则主机B告诉主机A在接受自己确认之前最多可以发送32,768字节的数据(以最大段大小或MSS的间隔) 。假设MSS为1460字节,主机A可以在耗尽主机B的接收窗口之前发送22个段。
当确认收到主机A发送的数据时,主机B可以调整其窗口大小。例如,如果上层应用程序仅处理了一半缓冲区,则主机B会将其窗口大小降低到16 KB(这时候主机A在接受到B的确认前最多发送16KB数据到B)。如果缓冲区仍然完全填满,主机B会将其窗口大小设置为零,表明它还不能接受更多数据,这时候主机A则停止发送数据。
在具有高带宽和极低延迟的LAN上,滑动窗口很少会满。但是,在高带宽,高延迟网络上,会出现一个有趣的现象:在发送方接受到接收方发出确认之前,并不能最大化利用带宽。
例如,假设在通过专用10 Mbps路径连接的两台主机之间建立TCP连接,单向延迟为80ms。两个主机都约定最大窗口大小为65,535字节(16位无符号整数的最大值)。我们可以计算在一个时间点在一个方向上传输的潜在数据量,带宽*延迟:10,000,000 bps除以每字节8位,乘以0.08秒等于100,000字节。
换句话说,如果主机A开始连续发送给主机B,它将在主机B接收到发送的第一个字节之前发送100,000个字节。但是由于约定的最大接收窗口只有65,535字节,所以主机A必须在发送65,535字节后停止发送,并等待来自主机B的确认。(为简单起见,我们的示例计算不考虑TCP和低层报头。)这种延迟浪费了潜在的吞吐量,不必要地增加了通过网络可靠传输数据所需的时间。创建TCP窗口缩放以解决此问题。
三、窗口缩放因子
窗口缩放在RFC 1072中引入并在RFC 1323中进行了改进。实际上,窗口缩放只是将16位窗口字段扩展为32位长度。解决方案是定义TCP选项以指定计数,通过该计数,TCP标头字段应按位移位以产生更大的值。
如上图 window size设置为5840字节,但是窗口缩放因子为7(window scale),也就是这时候最大实际窗口为 5840*128。window scale为1将字段的二进制值向左移位一位,使其加倍。计数为2将值向左移动两位,使其翻倍。计数为7(如上例所示)将该值乘以128.
窗口缩放选项(window scaling)可以在tcp握手时候在SYN分组中的连接期间仅发送一次。可以通过修改TCP标头中的窗口字段的值来动态调整窗口大小,但是在TCP连接的持续时间内,标度乘数保持静态。仅当两端都包含选项时,缩放才有效;如果只有连接的一端支持窗口缩放,则不会在任一方向上启用它。最大有效比例值为14(RFC 1323的2.3节为有兴趣的人提供了一些背景信息)。
回顾我们之前的示例,我们可以观察窗口缩放如何使我们能够更有效地进行网络传输(最大化利用网络带宽)。为了计算我们的理想窗口,我们将端到端延迟加倍以找到往返时间,并将其乘以可用带宽:2 * 0.08秒* 10,000,000 bps / 8 = 200,000字节。为了支持这种大小的窗口,主机B可以将其窗口大小设置为3,125,其window scaling因子为6(3,125左移6乘以200,000)。幸运的是,这些计算都是由现代TCP / IP堆栈实现自动处理,无须应用程序层面干预。
在应用实战中,我们的文件上传服务上传图片时候还好,但是上传大视频时候就发现很慢,一个8M的视频上传需要1分钟30s,我们增大tcpbuffer和开启了缩放因子调优后,8M视频只需要20s
四、参考
http://packetlife.net/blog/2010/aug/4/tcp-windows-and-window-scaling/
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/60883.html