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

Sma*_*ger 50 networking windows tcp

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

最初的报告是,上传到新服务器实例的速度比他们能做到的要慢得多。这在测试中和来自多个位置的情况下都得到证实;客户从他们的 Windows 系统看到稳定的 2-5Mbit/s 到主机。

iperf -s在 AWS 实例上爆发,然后从办公室的Windows客户端爆发:

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

  • iperf -c Windows SYN - 窗口 64kb,比例 1 - Linux SYN,ACK:窗口 14kb,比例:9 (*512) 使用默认 64kb 窗口缩放 iperf 窗口
  • iperf -c -w1M Windows 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
Run Code Online (Sandbox Code Playgroud)

没有任何强迫,它按预期扩展。这不可能是中间跃点或我们的本地交换机/路由器中的东西,似乎对 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
Run Code Online (Sandbox Code Playgroud)

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

编辑

我在这里发现了另一个可能相关的一致内容: 在此处输入图片说明

这是放大的 1MB 捕获的第一秒。随着窗口放大和缓冲区变大,您可以看到“慢启动”在起作用。然后就是这个约 0.2 秒的小平台,恰好在默认窗口 iperf 测试永远变平的时候。这当然可以扩展到更令人眼花缭乱的高度,但奇怪的是,在它这样做之前,缩放中有这种暂停(值是 1022 字节 * 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,因为有来自每个 OS 客户端的一个用于比较)

更新 2 - 6 月 30 日

哦,所以按照凯尔的建议,我启用了 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
Run Code Online (Sandbox Code Playgroud)

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

不过,我在这里确实有一个因果问题:图表是在服务器对客户端的 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 日

为了消除 Linux 服务器端,我启动了一个 Server 2012R2 实例并使用iperf(cygwin binary) 和NTttcp重复测试。

有了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
Run Code Online (Sandbox Code Playgroud)

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

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

更改iperf缓冲区长度确实会对时间序列图产生预期的差异(更多的垂直部分),但实际吞吐量没有变化。

Pat*_*Pat 15

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

请阅读:

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

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

...

这些算法适用于小 BDP和较小的接收窗口大小。但是,当您的 TCP 连接具有较大的接收窗口大小和较大的 BDP 时,例如在位于高速WAN 链接的两台服务器之间复制数据,往返时间为 100 毫秒,这些算法不会增加发送窗口足够快以充分利用连接的带宽

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

...

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

编辑 6/30/2014

看看CTCP是否真的“开启”

> netsh int tcp show global
Run Code Online (Sandbox Code Playgroud)

IE

在此处输入图片说明

PO 说:

如果我理解正确的话,这个设置会增加 拥塞窗口扩大 的速度,而不是它可以达到的最大大小

CTCP 主动增加发送窗口

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

复合TCP

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

这些算法适用于 LAN 媒体速度和较小的 TCP 窗口大小。但是,当您的 TCP 连接具有较大的接收窗口大小和较大的带宽延迟产品(高带宽和高延迟)时,例如在位于高速 WAN 链接上的两台服务器之间复制数据,往返时间为 100 毫秒时间,这些算法没有足够快地增加发送窗口以充分利用连接的带宽。例如,在具有 100 毫秒往返时间 (RTT) 的 1 吉比特每秒 (Gbps) WAN 链接上,发送窗口可能 需要长达一个小时才能最初增加到 接收方通告 大窗口大小,并且当有丢失的段时恢复

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

在 Microsoft 内部执行的测试中,对于具有 50 毫秒 RTT 的 1 Gbps 连接,大文件备份时间几乎减少了一半。具有更大带宽延迟乘积的连接可以具有更好的性能。CTCP 和接收窗口自动调整协同工作以提高链路利用率,并且可以为大带宽延迟产品连接带来显着的性能提升。

  • 作为对这个答案的补充,Server 2012/Win8.1 中的 Powershell 等效项是带有“-CongestionProvider”参数的“Set-NetTCPSetting”......它接受 CCTP、DCTCP 和默认值。Windows 客户端和服务器使用不同的默认拥塞提供程序。http://technet.microsoft.com/en-us/library/hh826132.aspx (3认同)

Kyl*_*ndt 12

澄清问题:

TCP有两个窗口:

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

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

在此处输入图片说明

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

在上图中,RWIN 保持打开状态这一事实证明了这一点,往返时间为 0.09 秒,RWIN 约为 500,000 字节,根据带宽延迟乘积,我们可以预期最大吞吐量为 (500000 /0.09) * 8 =~ 42 Mbit/s(并且在你的 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卸载至少是可能的。

  • 我已经报告了您的“答案”,因为您的这不是答案;我立即被否决了;现在我看到“人们”是如何为你的“无回答”投票的……真的很有趣 (2认同)

小智 5

@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 ));
}
Run Code Online (Sandbox Code Playgroud)

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

Windows 7 上的默认套接字缓冲区大小为 64k。以下是文档关于MSDN 上与吞吐量相关的套接字缓冲区大小的说明

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

好吧,等等等等,现在我们开始:

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

您最近使用 64k 窗口进行的 iperf 测试的平均吞吐量为 5.8Mbps。这是来自Wireshark 中的统计 > 摘要,它计算所有位。很可能,iperf 正在计算 5.7Mbps 的 TCP 数据吞吐量。我们在 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 的动态发送缓冲。默认情况下,TCP 的动态发送缓冲是启用的,除非应用程序在流套接字上设置 SO_SNDBUF 套接字选项。

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

netsh winsock show autotuning
Run Code Online (Sandbox Code Playgroud)

文档说您可以通过以下方式禁用它:

netsh winsock set autotuning off
Run Code Online (Sandbox Code Playgroud)

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

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

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

为什么在将数据发送到接收窗口中有足够空间的 Linux 机器时,您的发送缓冲区缩放比例没有超过默认的 64k?很好的问题。Linux 内核也有一个自动调整的 TCP 堆栈。就像 T-Pain 和 Kanye 一起做自动调谐二重唱一样,这听起来可能不太好。也许这两个自动调整 TCP 堆栈之间存在一些问题。

另一个人遇到了和您一样的问题,并且能够通过注册表编辑来修复它以增加默认发送缓冲区大小。不幸的是,这似乎不再起作用,至少当我尝试时它对我不起作用。

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

你可以:

  • 使用允许您设置发送缓冲区的应用程序,即窗口选项
  • 使用本地 Linux 代理
  • 使用远程 Windows 代理?
  • 用Microsof开个case哈哈哈哈哈哈哈哈
  • 啤酒

免责声明:我已经花了很多时间研究这个,据我所知,这是正确的和 google-fu。但我不会在我母亲的坟墓前发誓(她还活着)。