一文教会实战网络抓包和分析包( 十 )

  • 发送方收到零窗口通知后 , 就不再发送数据了 , 直到过了 3.4 秒后 , 发送了一个 TCP Keep-Alive 报文 , 也就是窗口大小探测报文;
  • 当接收方收到窗口探测报文后 , 就立马回一个窗口通知 , 但是窗口大小还是 0;
  • 发送方发现窗口还是 0 , 于是继续等待了 6.8(翻倍) 秒后 , 又发送了窗口探测报文 , 接收方依然还是回了窗口为 0 的通知;
  • 发送方发现窗口还是 0 , 于是继续等待了 13.5(翻倍) 秒后 , 又发送了窗口探测报文 , 接收方依然还是回了窗口为 0 的通知;
  •  
    可以发现 , 这些窗口探测报文以 3.4s、6.5s、13.5s 的间隔出现 , 说明超时时间会翻倍递增 。
    这连接暂停了 25s , 想象一下你在打王者的时候 , 25s 的延迟你还能上王者吗?
    #发送窗口的分析
     
    在 Wireshark 看到的 Windows size 也就是 " win = " , 这个值表示发送窗口吗?
     
    这不是发送窗口 , 而是在向对方声明自己的接收窗口 。
    你可能会好奇 , 抓包文件里有「Window size scaling factor」 , 它其实是算出实际窗口大小的乘法因子 , 「Window size value」实际上并不是真实的窗口大小 , 真实窗口大小的计算公式如下:
    「Window size value」 * 「Window size scaling factor」 = 「Caculated window size 」
    对应的下图案例 , 也就是 32 * 2048 = 65536 。
    一文教会实战网络抓包和分析包

    文章插图
     
    实际上是 Caculated window size 的值是 Wireshark 工具帮我们算好的 , Window size scaling factor 和 Windos size value 的值是在 TCP 头部中 , 其中 Window size scaling factor 是在三次握手过程中确定的 , 如果你抓包的数据没有 TCP 三次握手 , 那可能就无法算出真实的窗口大小的值 , 如下图:
    一文教会实战网络抓包和分析包

    文章插图
     
     
    如何在包里看出发送窗口的大小?
     
    很遗憾 , 没有简单的办法 , 发送窗口虽然是由接收窗口决定 , 但是它又可以被网络因素影响 , 也就是拥塞窗口 , 实际上发送窗口是值是 min(拥塞窗口 , 接收窗口) 。
     
    发送窗口和 MSS 有什么关系?
     
    发送窗口决定了一口气能发多少字节 , 而 MSS 决定了这些字节要分多少包才能发完 。
    举个例子 , 如果发送窗口为 16000 字节的情况下 , 如果 MSS 是 1000 字节 , 那就需要发送 1600/1000 = 16 个包 。
     
    发送方在一个窗口发出 n 个包 , 是不是需要 n 个 ACK 确认报文?
     
    不一定 , 因为 TCP 有累计确认机制 , 所以当收到多个数据包时 , 只需要应答最后一个数据包的 ACK 报文就可以了 。
    #TCP 延迟确认与 Nagle 算法
    当我们 TCP 报文的承载的数据非常小的时候 , 例如几个字节 , 那么整个网络的效率是很低的 , 因为每个 TCP 报文中都会有 20 个字节的 TCP 头部 , 也会有 20 个字节的 IP 头部 , 而数据只有几个字节 , 所以在整个报文中有效数据占有的比重就会非常低 。
    这就好像快递员开着大货车送一个小包裹一样浪费 。
    那么就出现了常见的两种策略 , 来减少小报文的传输 , 分别是:
     
    • Nagle 算法
    • 延迟确认
    Nagle 算法是如何避免大量 TCP 小数据报文的传输?
     
    Nagle 算法做了一些策略来避免过多的小数据报文发送 , 这可提高传输效率 。
    Nagle 伪代码如下:
    if 有数据要发送 { if 可用窗口大小 >= MSS and 可发送的数据 >= MSS { 立刻发送MSS大小的数据 } else { if 有未确认的数据 { 将数据放入缓存等待接收ACK } else { 立刻发送数据 } } }


    推荐阅读