【问题标题】:TCP Socket Sending Delays and RetransmissionTCP Socket 发送延迟和重传
【发布时间】:2010-06-29 22:24:17
【问题描述】:

我有一个 .NET 3.5 C# 应用程序,它向运行 sles 10 的 linux 机器发送 2000-6000 字节的数据包。这些机器在同一个子网上。

大约 90% 的时间,一切正常。 linux机器处理我的请求并在5-15ms内响应。但大约 10% 的时间,会有大约 200ms-800ms 的延迟。

查看linux机器上的日志,看来延迟到我头了。也就是说,如果我对 socket.Send(...) 的调用在 1:15:00.000 返回并且我在 1:15:00.210 收到响应,则 linux 机器上的日志说它在 1:15 收到了请求:00.200 然后在 10ms 内处理它。 (我正在使用 System.Diagnostics.Stopwatch 在我的机器上计时。)

为了调试,我使用wireshark 捕获了流量。这里是交通。在 8 号和 9 号之间发生 600 毫秒的延迟。 (137.34.210.108 是我的机器,137.34.210.95 是 linux 机器)。

"1","11:56:27.380318","137.34.210.95","137.34.210.108","TCP","20700 > 17479 [PSH, ACK] Seq=1 Ack=1 Win=32767 Len=76"
"2","11:56:27.380393","HewlettP_29:37:0f","Broadcast","ARP","Who has 137.34.210.95?  Tell 137.34.210.108"
"3","11:56:27.380558","HewlettP_29:39:93","HewlettP_29:37:0f","ARP","137.34.210.95 is at 00:1b:78:29:39:93"
"4","11:56:27.380564","137.34.210.108","137.34.210.95","TCP","17479 > 20700 [ACK] Seq=1 Ack=77 Win=65459 [TCP CHECKSUM INCORRECT] Len=0"
"5","12:04:48.096892","HewlettP_29:37:0f","Broadcast","ARP","Who has 137.34.210.95?  Tell 137.34.210.108"
"6","12:04:48.097216","HewlettP_29:39:93","HewlettP_29:37:0f","ARP","137.34.210.95 is at 00:1b:78:29:39:93"
"7","12:04:48.097229","137.34.210.108","137.34.210.95","TCP","17480 > 20600 [PSH, ACK] Seq=1 Ack=1 Win=64198 [TCP CHECKSUM INCORRECT] Len=458"
"8","12:04:48.097457","137.34.210.95","137.34.210.108","TCP","20600 > 17480 [ACK] Seq=1 Ack=4294964377 Win=32767 Len=0 SLE=1 SRE=459"
"9","12:04:49.700966","137.34.210.108","137.34.210.95","TCP","17479 > 20700 [ACK] Seq=1 Ack=77 Win=65459 [TCP CHECKSUM INCORRECT] Len=1460"
"10","12:04:49.701190","137.34.210.108","137.34.210.95","TCP","[TCP Retransmission] 17480 > 20600 [ACK] Seq=4294964377 Ack=1 Win=64198 [TCP CHECKSUM INCORRECT] Len=1460"
"11","12:04:49.703970","137.34.210.95","137.34.210.108","TCP","20600 > 17480 [ACK] Seq=1 Ack=4294965837 Win=32767 Len=0 SLE=1 SRE=459"
"12","12:04:49.703993","137.34.210.108","137.34.210.95","TCP","[TCP Retransmission] 17480 > 20600 [ACK] Seq=4294965837 Ack=1 Win=64198 [TCP CHECKSUM INCORRECT] Len=1460"
"13","12:04:49.704002","137.34.210.108","137.34.210.95","TCP","[TCP Retransmission] 17480 > 20600 [PSH, ACK] Seq=1 Ack=1 Win=64198 [TCP CHECKSUM INCORRECT] Len=458"
"14","12:04:49.704211","137.34.210.95","137.34.210.108","TCP","20600 > 17480 [ACK] Seq=1 Ack=459 Win=32767 Len=0"
"15","12:04:49.704215","137.34.210.95","137.34.210.108","TCP","[TCP Dup ACK 14#1] 20600 > 17480 [ACK] Seq=1 Ack=459 Win=32767 Len=0 SLE=1 SRE=459"
"16","12:04:49.705425","137.34.210.95","137.34.210.108","TCP","20700 > 17479 [PSH, ACK] Seq=77 Ack=1461 Win=32767 Len=44"

有人可以帮我解释一下吗?我看到正在发生重新传输。但我不确定为什么。交换机显示没有丢弃的数据包。而且就算丢包,为什么重传还要600ms?

我认为这 (http://support.microsoft.com/kb/328890) 可能与 200 毫秒的延迟有关,但我尝试更改 TcpAckFrequency 并没有帮助。

谢谢, 迈克

【问题讨论】:

    标签: c# networking sockets tcp


    【解决方案1】:

    让我们首先修剪一些 Wireshark 输出。我们可以在数据包 2、3、5 和 6 中折腾 ARP。看看其余的,你有两组流量。数据包 8 和 9 是两个不同的连接,因此您无法比较它们。然而,7、8 和 10 是一个连接的一部分,所以让我们检查一下。

    数据包 7 是 458 字节的数据发送到 Linux 机器,TCP 序列号为 1。但是,Linux 机器返回的 ACK 是 4294964377。这意味着 Wireshark 显示的是相对的 TCP 值,并且 Linux 机器不是为数据包 7 发送 ACK,而是为更早的数据包发送 ACK。然后,您的 PC 等待后续 ACK,如果没有收到,则重新传输所需的数据。在这种情况下,数据包 7 中的 458 个字节以及之前的 1002 个字节。这就是为什么数据包 10 的序列号与数据包 8 的 ACK 匹配的原因。

    很遗憾,这并没有告诉您数据被丢弃的原因。数据包 8 显示 Linux 框,表明它仍有完整的 32k 输入缓冲区可用于此连接(“Win=32767”)。

    【讨论】:

    • 非常感谢。我的错误是忘记了我同时向那台机器发送了两个数据包,一个在端口 20600 上,一个在 20700 端口上。所以我混淆了这两个对话。现在我想我的问题是:如何使重新传输更快?为什么较早数据包的重复确认与我端的重新传输之间的长时间停顿?我怎样才能加快速度?
    • 您能否同时在您的 PC 和 Linux 机器上运行捕获?这看起来像是一个数据包没有到达目的地的经典案例。我假设这个连接是通过带有某种交换机的 LAN。如果是这种情况,也许您还有其他一些工具可以监控整体网络拥塞。我不确定您是否可以更改重新传输超时,但无论如何,这似乎是在治疗症状而不是原因。
    • 实际上,通过增加 ARP 缓存超时,我确实设法大幅减少了今天的传输次数。我注意到,每当套接字上的发送间隔超过 2 分钟时,重新传输的可能性就会急剧增加。我用 2 分钟的时间寻找任何东西,发现 ARP 缓存并增加它,过去几个小时没有任何问题。但这仍然留下两个问题 - 1)为什么增加 ARP 缓存超时可以解决它? 2)有没有办法在仍然发生的时候缩短重新传输时间。
    • 啊,所以延迟与您的 PC 在 ARP 缓存条目到期时执行 ARP 解析有关。基本上,当条目过期时,PC 必须再次找出 Linux 机器的硬件地址,并将保留任何未决的传输,直到发生这种情况。您可以尝试使用一些方法来尽量减少问题:每分钟左右 ping Linux 一次,在您的 PC 上添加静态 ARP 条目,或者将缓存超时增加到 8 小时等一些愚蠢的量。但不适合实际部署。
    【解决方案2】:

    这仅显示 Linux 机器上的 TCP 数据包,但我建议使用“netstat -s”命令查看 ip 统计信息。重传的一个原因可能是套接字缓冲区溢出,这将在此命令中显示。

    【讨论】:

    • 这里是输出... IPv4 主动打开的 TCP 统计信息 = 14801 被动打开 = 1168 失败的连接尝试 = 116 重置连接 = 33 当前连接 = 接收的 46 个段 = 64272975 个已发送的段 = 20797063 个重新传输的段= 486 接收的 IPv4 数据报的 UDP 统计 = 25465 无端口 = 52039 接收错误 = 7098 发送的数据报 = 26223
    • 收到的 IPv4 统计数据包 = 64340244 收到的标头错误 = 0 收到的地址错误 = 3230 个转发的数据报 = 0 收到的未知协议 = 0 收到的数据包丢弃 = 38 个收到的数据包交付 = 64329718 输出请求 = 20827053
    • 路由丢弃 = 0 丢弃的输出数据包 = 0 输出数据包无路由 = 0 需要重组 = 15207 重组成功 = 5051 重组失败 = 2587 数据报分段成功 = 0 数据报分段失败 = 0 已创建分段 = 0
    • 感谢您的浏览,对于 netstat -s 输出的格式不佳感到抱歉。至于来自wireshark的日志,我相信这显示了两个方向。左边的IP是源,右边的IP是目的地。
    • Michael,这些统计数据是累积的,即机器启动的时间。您需要测量差异,以便我们获得更准确的数据。从统计数据中可以清楚地看出有重传。您是否增加了套接字接收缓冲区的大小? nagle 算法是否开启?
    【解决方案3】:

    我不记得 Windows 是否有它,但在 UNIX 上你会启用 TCP_NODELAY

    这会禁用 TCP 的 Nagle 算法,这会使系统等待一小段时间,以防更多数据将添加到传输缓冲区。

    int nodelay = 1;
    setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
    

    【讨论】:

    • Nagling 已禁用。 .NET sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 中的语法非常相似sock.NoDelay = true;
    猜你喜欢
    • 2013-04-11
    • 1970-01-01
    • 2021-01-28
    • 2018-10-12
    • 2018-07-08
    • 2021-07-01
    • 2018-04-11
    • 1970-01-01
    • 2012-04-01
    相关资源
    最近更新 更多