Windows TCP 窗口缩放过早达到稳定状态

Windows TCP 窗口缩放过早达到稳定状态

场景:我们有许多 Windows 客户端定期将大型文件 (FTP/SVN/HTTP PUT/SCP) 上传到距离约为 100-160 毫秒的 Linux 服务器。我们的办公室有 1Gbit/s 同步带宽,服务器要么是 AWS 实例,要么物理托管在美国数据中心。

最初的报告显示,上传到新服务器实例的速度比应有的速度要慢得多。这在测试中和从多个位置都得到了证实;客户从他们的 Windows 系统看到到主机的稳定速度为 2-5Mbit/s。

iperf -s我在 AWS 实例上爆发,然后从视窗办公室里的客户:

iperf -c 1.2.3.4

[  5] local 10.169.40.14 port 5001 connected with 1.2.3.4 port 55185
[  5]  0.0-10.0 sec  6.55 MBytes  5.48 Mbits/sec

iperf -w1M -c 1.2.3.4

[  4] local 10.169.40.14 port 5001 connected with 1.2.3.4 port 55239
[  4]  0.0-18.3 sec   196 MBytes  89.6 Mbits/sec

后一个数字在后续测试中可能会有很大差异(AWS 的变化),但通常在 70 到 130Mbit/s 之间,这足以满足我们的需求。通过 Wireshark 查看会话,我可以看到:

  • iperf -cWindows SYN - 窗口 64kb,比例 1 - Linux SYN,ACK:窗口 14kb,比例:9 (*512) iperf 窗口缩放,默认 64kb 窗口
  • iperf -c -w1MWindows SYN - Windows 64kb,比例 1 - Linux SYN,ACK:窗口 14kb,比例:9 iperf 窗口缩放,默认 1MB 窗口

显然,该链接可以维持这种高吞吐量,但我必须明确设置窗口大小才能使用它,而大多数实际应用程序都不允许我这样做。 TCP 握手在每种情况下都使用相同的起点,但强制的起点可以扩展

相反,从同一网络上的 Linux 客户端直接iperf -c(使用系统默认值 85kb)可以得到:

[  5] local 10.169.40.14 port 5001 connected with 1.2.3.4 port 33263
[  5]  0.0-10.8 sec   142 MBytes   110 Mbits/sec

无需任何强制,它就会按预期扩展。这不可能是中间跳数或本地交换机/路由器的问题,而且似乎对 Windows 7 和 8 客户端都有影响。我读过很多关于自动调整的指南,但这些指南通常都是关于完全禁用扩展,以解决糟糕的家庭网络套件问题。

有人能告诉我这里发生了什么,并给我一个修复方法吗?(最好是我可以通过 GPO 粘贴到注册表中的东西。)

笔记

有问题的 AWS Linux 实例应用了以下内核设置sysctl.conf

net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.ipv4.tcp_rmem = 4096 1048576 16777216
net.ipv4.tcp_wmem = 4096 1048576 16777216

我使用dd if=/dev/zero | nc重定向到/dev/null服务器端来排除iperf和消除任何其他可能的瓶颈,但结果大致相同。使用ncftp(Cygwin、Native Windows、Linux) 进行的测试与上述 iperf 在其各自平台上进行的测试方式大致相同。

编辑

我在这里发现了另一件可能相关的一致的事情: 在此处输入图片描述

这是 1MB 截图的第一秒,已放大。你可以看到慢启动随着窗口扩大,缓冲区变大,这个过程会逐渐进行。然后会出现一个大约 0.2 秒的微小停滞期确切地此时,默认窗口 iperf 测试将永远保持平稳。当然,这个窗口可以扩展到更令人眼花缭乱的高度,但奇怪的是,在达到这个高度之前,缩放过程中有一个暂停(值为 1022bytes * 512 = 523264)。

更新——6 月 30 日。

跟进各种回应:

  • 启用 CTCP - 这没什么区别;窗口缩放是相同的。(如果我理解正确的话,此设置会增加拥塞窗口扩大的速率,而不是增加其可以达到的最大大小)
  • 启用 TCP 时间戳。- 这里也没有变化。
  • Nagle 算法 - 这很有道理,至少这意味着我可以忽略图表中的特定波动作为问题的任何迹象。
  • pcap 文件:Zip 文件可从此处获取:https://www.dropbox.com/s/104qdysmk01lnf6/iperf-pcaps-10s-Win%2BLinux-2014-06-30.zip(使用 bittwiste 匿名化,提取到~150MB,因为每个操作系统客户端都有一个用于比较)

更新 2 - 6 月 30 日

哦,所以按照 Kyle 的建议,我启用了 ctcp 并禁用了烟囱卸载:TCP 全局参数

----------------------------------------------
Receive-Side Scaling State          : enabled
Chimney Offload State               : disabled
NetDMA State                        : enabled
Direct Cache Acess (DCA)            : disabled
Receive Window Auto-Tuning Level    : normal
Add-On Congestion Control Provider  : ctcp
ECN Capability                      : disabled
RFC 1323 Timestamps                 : enabled
Initial RTO                         : 3000
Non Sack Rtt Resiliency             : disabled

但遗憾的是,吞吐量没有变化。

不过,我这里有一个因果问题:这些图表是服务器向客户端发送的 ACK 中设置的 RWIN 值。对于 Windows 客户端,我是否可以正确地认为 Linux 不会将此值扩展到该低点以下,因为客户端有限的 CWIN 甚至会阻止该缓冲区被填充?Linux 人为限制 RWIN 是否还有其他原因?

注意:我曾尝试打开 ECN;但没有任何变化。

更新 3 - 6 月 31 日。

禁用启发式和 RWIN 自动调整后没有变化。已将英特尔网络驱动程序更新为最新版本 (12.10.28.0),并使用可通过设备管理器选项卡显示功能调整的软件。该卡是 82579V 芯片组板载 NIC -(我将对使用 realtek 或其他供应商的客户进行更多测试)

暂时关注 NIC,我尝试了以下方法(主要是排除不可能的罪魁祸首):

  • 将接收缓冲区从 256 增加到 2k,将传输缓冲区从 512 增加到 2k(现在均已达到最大值)- 无变化
  • 禁用所有 IP/TCP/UDP 校验和卸载。 - 无变化。
  • 禁用大型发送卸载 - Nada。
  • 已关闭 IPv6、QoS 调度——现在。

更新 3 - 7 月 3 日

iperf为了消除 Linux 服务器端的问题,我启动了一个 Server 2012R2 实例,并使用cygwin 二进制文件重复了测试。远程控制协议

有了iperf,我必须明确-w1m指定两个都连接扩展至超过 ~5Mbit/s 之前,必须先进行端到端测试。(顺便说一句,我可以进行测试,延迟为 91ms 的 ~5Mbits 的 BDP 几乎正好是 64kb。找出极限...)

ntttcp 二进制文件现在显示出这样的限制。ntttcpr -m 1,0,1.2.3.5在服务器和ntttcp -s -m 1,0,1.2.3.5 -t 10客户端上使用时,我可以看到更好的吞吐量:

Copyright Version 5.28
Network activity progressing...


Thread  Time(s) Throughput(KB/s) Avg B / Compl
======  ======= ================ =============
     0    9.990         8155.355     65536.000

#####  Totals:  #####

   Bytes(MEG)    realtime(s) Avg Frame Size Throughput(MB/s)
================ =========== ============== ================
       79.562500      10.001       1442.556            7.955

Throughput(Buffers/s) Cycles/Byte       Buffers
===================== =========== =============
              127.287     308.256      1273.000

DPCs(count/s) Pkts(num/DPC)   Intr(count/s) Pkts(num/intr)
============= ============= =============== ==============
     1868.713         0.785        9336.366          0.157

Packets Sent Packets Received Retransmits Errors Avg. CPU %
============ ================ =========== ====== ==========
       57833            14664           0      0      9.476

8MB/s 达到了我在 中使用大窗口时获得的水平iperf。但奇怪的是,1273 个缓冲区中的 80MB 又 = 64kB 缓冲区。进一步的 wireshark 显示从服务器返回的良好可变 RWIN(比例因子 256),客户端似乎满足该要求;因此 ntttcp 可能误报了发送窗口。

更新 4 - 7 月 3 日

应@karyhead 的要求,我做了更多的测试并生成了更多的捕获,如下: https://www.dropbox.com/s/dtlvy1vi46x75it/iperf%2Bntttcp%2Bftp-pcaps-2014-07-03.zip

  • 另外两个iperf,都是从 Windows 到与之前相同的 Linux 服务器(1.2.3.4):一个具有 128k 套接字大小和默认 64k 窗口(再次限制为 ~5Mbit/s),另一个具有 1MB 发送窗口和默认 8kb 套接字大小。(可更高扩展)
  • 从同一 Windows 客户端到 Server 2012R2 EC2 实例 (1.2.3.5) 的一次ntttcp跟踪。在这里,吞吐量扩展得很好。注意:NTttcp 在打开测试连接之前在端口 6001 上做了一些奇怪的事情。不确定那里发生了什么。
  • 一个 FTP 数据跟踪,使用 Cygwin 上传 20MB/dev/urandom到几乎相同的 Linux 主机 (1.2.3.6) ncftp。同样存在限制。使用 Windows Filezilla 的模式大致相同。

改变iperf缓冲区长度确实会对时间序列图产生预期的影响(更多的垂直部分),但实际吞吐量保持不变。

答案1

您是否尝试过启用复合TCP(CTCP)在您的 Windows 7/8 客户端中。

请阅读:

提高高 BDP 传输的发送方性能

http://technet.microsoft.com/en-us/magazine/2007.01.cableguy.aspx

...

这些算法适用于小型 BDP以及较小的接收窗口大小。但是,当您的 TCP 连接具有较大的接收窗口大小和大型 BDP比如在高速网络上的两台服务器之间复制数据WAN 链路往返时间为 100ms,这些算法增加发送窗口的速度不够快,无法充分利用连接的带宽

为了在这些情况下更好地利用 TCP 连接的带宽,下一代 TCP/IP 堆栈包括复合 TCP (CTCP)。CTCP 更积极地增加发送窗口 具有较大接收窗口大小和 BDP 的连接。CTCP 尝试通过监控延迟变化和丢失来最大化此类连接的吞吐量。此外,CTCP 确保其行为不会对其他 TCP 连接产生负面影响。

...

在运行 Windows Server 2008 的计算机中,CTCP 默认启用;在运行 Windows Vista 的计算机中,CTCP 默认禁用。您可以使用该netsh interface tcp set global congestionprovider=ctcp命令启用 CTCP。您可以使用该命令禁用 CTCP netsh interface tcp set global congestionprovider=none

编辑 2014/6/30

看看 CTCP 是否真的“开启”

> netsh int tcp show global

IE

在此处输入图片描述

PO 说:

如果我理解正确的话,此设置会增加 拥塞窗口被放大,而不是最大尺寸 它可以达到

CTCP 积极增加发送窗口

http://technet.microsoft.com/en-us/library/bb878127.aspx

复合TCP

现有的防止发送 TCP 对等体压垮网络的算法称为慢启动和拥塞避免。这些算法增加了发送方在连接上最初发送数据时以及从丢失的段中恢复时可以发送的段数量(称为发送窗口)。慢启动会将发送窗口增加一个完整的 TCP 段,无论是对于收到的每个确认段(对于 Windows XP 和 Windows Server 2003 中的 TCP)还是对于确认的每个段(对于 Windows Vista 和 Windows Server 2008 中的 TCP)。拥塞避免会将发送窗口增加一个完整的 TCP 段,无论对于确认的每个完整数据窗口。

这些算法在 LAN 媒体速度和较小的 TCP 窗口大小下效果很好。但是,当您的 TCP 连接具有较大的接收窗口大小和较大的带宽延迟乘积(高带宽和高延迟)时,例如在往返时间为 100 毫秒的高速 WAN 链路上的两台服务器之间复制数据,这些算法无法足够快地增加发送窗口以充分利用连接的带宽。例如,在往返时间为 100 毫秒的 1 千兆位每秒 (Gbps) WAN 链路上,它可以 发送窗口最初需要一个小时才能增加到 接收方通告的大窗口大小,以及何时恢复 有丢失的片段。

为了更好地利用带宽在这些情况下,下一代 TCP/IP 堆栈包括复合TCP (CTCP)。对于接收窗口较大且带宽延迟积较大的连接,CTCP 会更积极地增加发送窗口。CTCP 会尝试通过以下方式最大化此类连接的吞吐量:监控延迟变化和丢失。CTCP 还确保其行为不会对其他 TCP 连接产生负面影响。

在 Microsoft 内部进行的测试中,对于 1 Gbps 连接和 50ms RTT,大文件备份时间减少了近一半。带宽延迟积更大的连接可以实现更好的性能。CTCP 和接收窗口自动调整协同工作以提高链路利用率,并可为大带宽延迟积连接带来显著的性能提升。

答案2

澄清问题:

TCP 有两个窗口:

  • 接收窗口:缓冲区中剩余多少字节。这是接收器施加的流量控制。您可以在 wireshark 中看到接收窗口的大小,因为它由 TCP 标头内的窗口大小和窗口缩放因子组成。TCP 连接的两端都会公布其接收窗口,但通常您关心的是接收大量数据的那一个。在您的例子中,它是“服务器”,因为客户端正在上传到服务器
  • 拥塞窗口。这是发送方施加的流量控制。它由操作系统维护,不会显示在 TCP 标头中。它控制发送数据的速率。

在您提供的捕获文件中。我们可以看到接收缓冲区从未溢出:

在此处输入图片描述

我的分析是,发送方发送速度不够快,因为发送窗口(又称拥塞控制窗口)的打开程度不足以满足接收方的 RWIN。简而言之,接收方说“给我更多”,而当 Windows 是发送方时,它发送速度不够快。

事实证明,上图中 RWIN 保持打开状态,往返时间为 0.09 秒,RWIN 约为 500,000 字节,根据带宽延迟积,我们预计最大吞吐量为 (500000/0.09) * 8 =~ 42 Mbit/s(而您在 win to Linux 捕获中只获得了大约 ~5)。

如何修复它?

我不知道。interface tcp set global congestionprovider=ctcp听起来对我来说这是正确的做法,因为它会增加发送窗口(这是拥塞窗口的另一个术语)。你说这不起作用。所以只是为了确保:

  1. 启用此功能后你重新启动了吗?
  2. 烟囱卸载功能是否已打开?如果已打开,可以尝试将其关闭。我不知道启用此功能后究竟会卸载哪些内容,但如果控制发送窗口是其中之一,则启用此功能后拥塞提供程序可能不起作用...我只是猜测...
  3. 另外,我认为这可能是 Windows 7 之前的版本,但您可以尝试在 HKEY_LOCAL_MACHINE-System-CurrentControlSet-Services-AFD-Parameters 中添加和使用名为 DefaultSendWindow 和 DefaultReceiveWindow 的两个注册表项。如果这些都有效,您可能已经关闭了 ctcp。
  4. 还有另一个猜测,尝试检查一下netsh interface tcp show heuristics。我认为那可能是 RWIN,但它没有说明,所以也许可以尝试禁用/启用它,以防它影响发送窗口。
  5. 另外,请确保您的测试客户端上的驱动程序是最新的。也许有些东西坏了。

我会首先关闭所有卸载功能,尝试所有这些实验,以消除网络驱动程序正在重写/修改某些内容的可能性(禁用卸载时请留意 CPU)。TCP_OFFLOAD_STATE_DELEGATED 结构似乎至少暗示 CWnd 卸载至少是可能的。

答案3

@Pat 和 @Kyle 提供了一些很棒的信息。一定要关注 @Kyle 的解释TCP 接收和发送窗口,我认为这方面存在一些混淆。更令人困惑的是,iperf 在设置中使用了术语“TCP 窗口”,-w这是一个与接收、发送或整体滑动窗口有关的含糊不清的术语。它实际上所做的是设置(客户端)实例的套接字发送缓冲区-c和(服务器)实例上的套接字接收缓冲区-s。在src/tcp_window_size.c

if ( !inSend ) {
    /* receive buffer -- set
     * note: results are verified after connect() or listen(),
     * since some OS's don't show the corrected value until then. */
    newTCPWin = inTCPWin;
    rc = setsockopt( inSock, SOL_SOCKET, SO_RCVBUF,
                     (char*) &newTCPWin, sizeof( newTCPWin ));
} else {
    /* send buffer -- set
     * note: results are verified after connect() or listen(),
     * since some OS's don't show the corrected value until then. */
    newTCPWin = inTCPWin;
    rc = setsockopt( inSock, SOL_SOCKET, SO_SNDBUF,
                     (char*) &newTCPWin, sizeof( newTCPWin ));
}

正如 Kyle 所说,问题不在于 Linux 机器上的接收窗口,而是发送方没有充分打开发送窗口。并不是它打开得不够快,只是上限为 64k。

Windows 7 上的默认套接字缓冲区大小为 64k。以下是文档中关于套接字缓冲区大小与吞吐量的关系微软

使用 Windows 套接字通过 TCP 连接发送数据时,为了实现最高吞吐量,在 TCP 中保留足够数量的未完成数据(已发送但尚未确认)非常重要。实现 TCP 连接最佳吞吐量的未完成数据量的理想值称为理想发送积压 (ISB) 大小。ISB 值是 TCP 连接的带宽延迟乘积与接收方公布的接收窗口(以及部分网络拥塞量)的函数。

好的,现在我们开始吧:

一次执行一个阻塞或非阻塞发送请求的应用程序通常依靠 Winsock 的内部发送缓冲来实现不错的吞吐量。给定连接的发送缓冲区限制由 SO_SNDBUF 套接字选项控制。对于阻塞和非阻塞发送方法,发送缓冲区限制决定了 TCP 中保留多少数据如果连接的 ISB 值大于发送缓冲区限制,则连接上实现的吞吐量将不会是最佳的。

您最近使用 64k 窗口进行的 iperf 测试的平均吞吐量为 5.8Mbps。这是统计信息 > 摘要在 Wireshark 中,它计算所有位。同样,iperf 计算的 TCP 数据吞吐量为 5.7Mbps。我们在 FTP 测试中也看到了同样的性能,~5.6Mbps。

64k 发送缓冲区和 91ms RTT 的理论吞吐量为....5.5Mbps。对我来说已经足够接近了。

如果我们查看您的 1MB 窗口 iperf 测试,tput 为 88.2Mbps(仅 TCP 数据为 86.2Mbps)。1MB 窗口的理论 tput 为 87.9Mbps。同样,对于政府工作来说已经足够接近了。

这表明,发送套接字缓冲区直接控制发送窗口,并与另一端的接收窗口一起控制吞吐量。通告的接收窗口有空间,因此我们不受接收器的限制。

等等,自动调整业务怎么样?Windows 7 不是自动处理这些事情吗?如前所述,Windows 确实处理接收窗口的自动缩放,但它也可以动态处理发送缓冲区。让我们回到 MSDN 页面:

Windows 7 和 Windows Server 2008 R2 添加了 TCP 动态发送缓冲。默认情况下,除非应用程序在流套接字上设置了 SO_SNDBUF 套接字选项,否则会启用 TCP 动态发送缓冲。

iperfSO_SNDBUF在使用该-w选项时使用,因此动态发送缓冲将被禁用。但是,如果您不使用,-w则它不会使用SO_SNDBUF。动态发送缓冲应该默认启用,但您可以检查:

netsh winsock show autotuning

文档说你可以使用以下方法禁用它:

netsh winsock set autotuning off

但这对我来说不起作用。我不得不更改注册表并将其设置为 0:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters\DynamicSendBufferDisable

我认为禁用此功能不会有帮助;这只是一个仅供参考。

为什么在向接收窗口中有足够空间的 Linux 机器发送数据时,发送缓冲区没有扩展到默认的 64k 以上?好问题。Linux 内核也有一个自动调整的 TCP 堆栈。就像 T-Pain 和 Kanye 一起进行自动调整二重唱一样,听起来可能不太好。也许这两个自动调整的 TCP 堆栈在相互通信时存在一些问题。

另一个人遇到了和你一样的问题,可以通过编辑注册表来增加默认发送缓冲区大小来解决。不幸的是,这似乎不再起作用了,至少在我尝试的时候它对我来说不起作用。

此时,我认为很明显限制因素是 Windows 主机上的发送缓冲区大小。鉴于它似乎没有正常地动态生长,女孩该怎么办?

你可以:

  • 使用允许您设置发送缓冲区(即窗口选项)的应用程序
  • 使用本地 Linux 代理
  • 使用远程 Windows 代理?
  • 向微软投诉哈哈哈哈哈哈哈哈
  • 啤酒

免责声明:我花了很多时间研究这个问题,据我所知和谷歌搜索,这是正确的。但我不会对我母亲的坟墓发誓(她还活着)。

答案4

阅读完答案中的所有分析后,这个问题听起来很像你可能正在运行 Windows7/2008R2 又名 Windows 6.1

Windows 6.1 中的网络堆栈(TCP/IP 和 Winsock)存在严重缺陷,包含大量错误和性能问题,自 6.1 首次发布以来,微软最终经过多年的修补解决了这些问题。

应用这些修补程序的最佳方法是手动筛选 support.microsoft.com 上的所有相关页面,并手动请求和下载网络堆栈修补程序的 LDR 版本(有几十个这样的版本)。

要查找相关的修补程序,您必须使用 www.bing.com 进行以下搜索查询 site:support.microsoft.com 6.1.7601 tcpip.sys

您还需要了解 LDR/GDR 修补程序序列在 Windows 6.1 中的工作方式

我通常会维护自己的 Windows 6.1 LDR 修复程序列表(不仅仅是网络堆栈修复程序),然后主动将这些修复程序应用于我遇到的任何 Windows 6.1 服务器/客户端。定期检查新的 LDR 修补程序是一项非常耗时的任务。

幸运的是,微软已经停止在较新的操作系统版本中使用 LDR 修补程序的做法,现在可以通过微软的自动更新服务获得错误修复。

更新:这只是 Windows7SP1 中众多网络漏洞的一个例子 -https://support.microsoft.com/en-us/kb/2675785

更新2:这是另一个修补程序,它添加了一个 netsh 开关,以在第二次重新传输 SYN 数据包后强制缩放窗口(默认情况下,在重新传输 2 个 SYN 数据包后禁用窗口缩放)https://support.microsoft.com/en-us/kb/2780879

相关内容