计算机网络协议第十章, TCP 协议之滑动窗口 2017 年 8 月 1 日 10:52
标签: window 滑动窗口 tcp windowscale 2015-03-28 10:29 447 人阅读
评论 (0) 收藏
举报 分类: 计算机网络协议( 8 ) 版权声明:本文为博主原创文章,未经博主允许不得转载。 目录 (?)[+] 本章围绕 TCP 协议的滑动窗口这一主题展开讨论,第一部分主要是对 TCP 滑动窗口的基础工作原理进行阐述,第二部分会深入的理解滑动窗口,第三部分会根据一个工作中实际遇到的例子来理解滑动窗口。
背景介绍 TCP 协议提供可靠的数据传输,因此必须解决网络传输中带来的丢包和包乱序问题。可想而知 ,TCP 协议栈需要一个接收缓存来处理接收到的数据报文,以保证其包顺序和完整性后提供给上层应用系统。 TCP 协议栈需要清楚网络实际的数据处理带宽和数据处理速度,以避免 TCP 接收缓存占用过多内存和匹配接收方实际处理能力,因此引入滑动窗口( Sliding Window )已通知发送方“接收方的最大接收能力”。 在阅读本文之前,希望你有一些 TCP 基础知识,了解 TCP 三步握手和 TCP 其他一些 Flag 标记的意义,这部分的工作可以阅读《 TCP/IP 卷一》。也可以阅读本博客的相关文章。
滑动窗口基础 这节主要讲述滑动窗口在 TCP 传输过程中如何体现的。 下图是 TCP 三次握手的 SYN 报文: 可以看出滑动窗口的大小是 65535 字节,这是 TCP 三步握手中进行必要工作,经过 TCP 三步握手,双方能够感知对方的滑动窗口大小的初始值,用于控制向对方发送最大字节数,避免对方无法处理接收导致的丢包。 上图还可以看出使用 Syn 包中使用 Window scale 选项。 Window scale 在 TCP 协议中定义是用于放大滑动窗口数值的途径。我们知道 TCP 中滑动窗口大小是有 2 个字节组成,也就是最大数 65535 ,后来发现最大值不够用,因此需要 Window scale 来对其扩充,如果使用 Window scale 选项后,滑动窗口的实际大小为 window size * 2^window scale 。上图中 windowscale 为 3 ,表示 8 倍。
下图是 TCP 传输过程中的 ACK 报文: 前面我们了解到 TCP 三步握手后,传输双方能够获取对方的滑动窗口初始值,然而发送方发送一些数据给对方之后,如何再次或者对方的滑动窗口,上图已经说明是如何传递滑动窗口了, TCP 通过 Ack 报文向对方反馈自己的滑动窗口,如果滑动窗口为 0 ,发送方就应该暂停发送数据,等待对方滑动窗口恢复后在进行发送。可参见《 TCP/IP 卷一》图 20-3 。 滑动窗口深入 下图描述 TCP 协议栈有关滑动窗口几个重要变量。 *LastByteRead 指向 TCP 接收 缓存 当前读到的位置。可以理解为上层应用系统最后一次 read 到的位置。 *NextByeExpected 表示 TCP 接收 缓存最后可读的位置。可以理解为最后一个连续、完整的数据报文。 *LastByteRcvd 表示收到的最新报文段的位置。前面一段空白部分就是说明前面报文丢掉或者还未达到。 滑动窗口的大小为:最大接收缓存
-
(LastByteRcvd
-
LastByteRead) 。可以简单这么理解。 下图描述发送方滑动窗口的示意图:
上图以颜色的方式分为 4 个部分,黑色框框就是滑动窗口,其大小由接收方控制。 * 第一部分,表示已经发送并且接收方已经回复 Ack 的数据。 * 第二部分,表示发送但是未收到 Ack 的数据。 * 第三部分,表示还未发送出去的数据,但是接收方还有空间。 * 第四部分,表示不能发送,接收方没有空间。 对比上图,红色部分表示新增加的部分表示接收方已经确认接收的数据。因此滑动窗口(黑色框框)的左边界向右运动,称为合拢运动。 第三部分用红色框框标记的为新的可用的数据,表示接收方已经释放相应的接收缓存(可以理解为上层应用程序 read 过该数据了),因此产生了新的可以空间。滑动窗口(黑色框框)的右边界相对上图也向右移动,称为张开运动。可参见《 TCP/IP 卷一 》图 20-5 ,图 20-6 。 假设接收方回复【 31-36 】 Ack 报文段中的滑动窗口值在变小(或者干脆为 0 ),说明接收方 TCP 协议栈已经确认接收这部分数据,但是应用程序还未读取到该数据,这种情况下,滑动窗口的右边界就不会向右移动,也就是说不会做张开运动。此处假设是进一步说明滑动窗口的张开运动是依赖于接收方上报的滑动窗口值,而不是依赖 Ack 确认哪些数据报文已经被接收。 因此这种滑动窗口的合拢和张开运动是受接收方所控制,因为其依赖于接收方的 Ack 确认和接收方滑动窗口值的变化。
滑动窗口之案例 案例背景 有一个信令服务 Server ,有一个客户端 Client 。 client 分别运行在两种设备上,并且分别为 window xp 和 window 2003 的操作系统。 现象是其中 xp 运行 client 能够正常工作, 2003 运行的 client 出现一会连接一会断开的现象,而且反复如此无法正常工作。 问题分析 Server 和 window 2003 的 client
三步握手和注册信令都完成,但是后续的信令却迟迟无法得到回复,因此 server 端超时过后会主动断开连接,这就是解释了为什么反复断开连接的原因。 但是为什么相同的程序在 window 2003 上运行却数据迟迟无法回复呢? 下面抓取 window 2003 client 的三步握手报文的 SYN,ACK 分节,是 server 端回复的报文。 可以看出 window scale=12,
滑动窗口的实际值应该是需要乘以 2 的 12 次方,就是乘以 4096
上图是 11 号包的具体内容,我们看到 windowsize 对应的二进制数据是 0x0010, 然后乘以 4096 ( 2 的 12 次方),也就是说服务端的滑动窗口大小是 16*4096=65536 计算得出的。 但是我们看到 13 号报文中等待 5 秒钟才发送 16 个字节,而且这 16 个字节并不是完整业务的回应数据,只是数据开头的 16 字节,到此我们可以假设 window2003 的协议栈应该是没有正确的解析服务器 [syn,ack] 端中的 windowscale ,认为服务的滑动窗口大小是 16 字节,因此等待了 5 秒才发送 16 字节的报文。通过排查后续所有报文都有这个规律。
问题总结 该问题是由于 window 2003 的网络内核对 TCP windowscale 选项没有处理导致的,认为服务端滑动窗口的只有 16 个字节,而不是 16*4096 个字节,因此才等待了 5 秒之后才发送 16 个字节的报文,因此导致了服务端对数据报文迟迟没有响应。 linux 中通过 /proc/sys et/ipv4/tcp_window_scaling
参数可以取消 window scaling 的机制,经过测试发送通信正常,也再次印证了上面的结论。 在 socket 编程中是无法设置 window scaliing 的,这个参数又是如何控制的呢? 引用 TCP/IP 卷一中的 20.4 小节原文:“插口 API 允许进程设置发送和接受缓存的大小,接受缓存的大小是该连接上所能够通告的最大窗口大小”。通过实验我们如果在 connect 调用之前设置 RCV_BUFF 选项,在调用 connect 会影响 Syn 段的滑动窗的大小,如果 RCV_BUFF 值大于 65535 肯定会使用 windowscale 选项。 其实也很好理解 connect 调用会触发 SYN 分节,因此之前设置才会影响滑动窗口的初始值,而滑动窗口只有 16 个 bit, 因此如果大于 65535 肯定需要使用 window scale 选项才能使滑动窗口大于 65535 ,这也是 windowscale 选项被设计出来的目的。 小结 本章讨论了滑动窗口设计的目的,传递滑动窗口的方式,滑动窗口计算方式,发送方滑动窗口运动方式几个部分。通过以上几个部分的描述,希望能够让入门者有所收获。通过一个实际工作中遇到的问题,希望能够帮助对 TCP 滑动窗口的理解。
来自
< http://blog.csdn.net/shenxin870409/article/details/44699163
已使用 Microsoft OneNote 2016 创建。