【问题标题】:Why Is This Code Buffering TCP Output When I Told it Not To?当我告诉它不要时,为什么这段代码会缓冲 TCP 输出?
【发布时间】:2012-01-11 06:02:53
【问题描述】:

这是我用来测试嵌入式产品上的网络服务器的代码,当 HTTP 请求通过多个 TCP 数据包分段进入时,该产品表现不佳:

/* This is all within a loop that cycles size_chunk up to the size of the whole 
 * test request, in order to test all possible fragment sizes. */
TcpClient client_sensor = new TcpClient(NAME_MODULE, 80);    
client_sensor.Client.NoDelay = true;    /* SHOULD force the TCP socket to send the packets in exactly the chunks we tell it to, rather than buffering the output. */
/* I have also tried just "client_sensor.NoDelay = true, with no luck. */
client_sensor.Client.SendBufferSize = size_chunk; /* Added in a desperate attempt to fix the problem before posting my shameful ignorance on stackoverflow. */
for (int j = 0; j < TEST_HEADERS.Length; j += size_chunk)
{
    String request_fragment = TEST_HEADERS.Substring(j, (TEST_HEADERS.Length < j + size_chunk) ? (TEST_HEADERS.Length - j) : size_chunk);
    client_sensor.Client.Send(Encoding.ASCII.GetBytes(request_fragment));     
    client_sensor.GetStream().Flush();   
}
/* Test stuff goes here, check that the embedded web server responded correctly, etc.. */

查看 Wireshark,我看到只有一个 TCP 数据包发出,其中包含整个测试标头,而不是我期望的大约 header length / chunk size 数据包。我之前使用过 NoDelay 来关闭 Nagle 算法,它通常像我期望的那样工作。 NoDelayhttp://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.nodelay%28v=vs.90%29.aspx 的在线文档在其相关代码示例中明确指出“在调用 NetworkStream.Write 时立即发送数据”,所以我认为我一直在正确使用它。

无论我是否单步执行代码,都会发生这种情况。 .NET 运行时是否优化了我的数据包碎片?

我正在运行 x64 Windows 7、.NET Framework 3.5、Visual Studio 2010。

【问题讨论】:

  • 我想知道 WireShark 是否会在数据包在线后合并数据包。在这种情况下它似乎根本不适用,但它显然确实有一些 reassembly 功能。这似乎不太可能,我可能只是在浪费您的时间,建议您将其视为一种可能性。
  • 我建议不要使用诸如“nanny-state”之类的贬义词,直到您确定您正确使用了系统。上马的第一条规则是,“确保你的不只是一头驴。”
  • @KennetBelenky,请注意,我已经删除了有问题的文字。不过我只是在开玩笑=)
  • @SamSkuce 谢谢和NP。我只是有点生气,因为人们通常没有意识到他们可以避免多少行烦人的样板 C++,以换取.Net 偶尔的怪异。话虽如此,我已经多次将头撞在 .Net 上,但每次都证明我是没有掌握全局的人。

标签: c# http tcp


【解决方案1】:

TcpClient.NoDelay 并不意味着字节块不会聚合到单个数据包中。这意味着字节块不会为了聚合成单个数据包而延迟。

如果要强制数据包边界,请使用Stream.Flush

【讨论】:

  • 不走运。 TcpClient.GetStream().Flush 的文档说这只是“供将来使用”。奇怪的是,当我不将其嵌入循环时,例如当 NoDelay 为 True 时,通过套接字写入两个常量缓冲区,即使在连续的行之间没有代码延迟,这也符合我过去的预期。不过谢谢!
  • 您确定您的套接字处于同步模式(阻塞 = false)吗?如果您的套接字处于非阻塞模式,您的写入可能会聚合成单个数据包。如果在套接字仍在刷新先前的写入时发生了两次后续写入,则会发生这种情况。
  • TCP 是一种流协议,这就是为什么您不能假设可能存在“单个数据包”的原因。如果您发送 4 个字节,然后发送 6 个字节 - 另一端的对等方可以接收 3 个字节,然后再调用接收另外 7 个字节。
  • @VadymStetsiak,是的,这就是您在阅读 TCP 时应该采用的方式,但是 NoDelay 属性的文档(现在在问题文本中链接)清楚地指出将属性设置为true 应该导致在单独的数据包中发送字节,就像你写它们一样。
  • @KennetBelenky,当我设置阻塞 = false 时,当我调用发送函数时,它会出现“不允许在非阻塞套接字上”的异常。我可能还需要做一些其他事情才能使其在非阻塞模式下工作,但我认为我现在的计划是在星期一尝试用本机 C++ 代码重写它。感谢您的所有帮助!
【解决方案2】:

Grr。是我的防病毒软件妨碍了我。最近的更新导致它开始通过缓冲所有输出来干扰向端口 80 发送 HTTP 请求,直到看到最终的“\r\n\r\n”标记,而不管操作系统如何尝试处理出站 TCP交通。我应该先检查一下,但是我多年来一直在使用同一个防病毒程序,以前从未遇到过这个问题,所以我什至没有想到。当我禁用防病毒软件时,一切正常。

【讨论】:

  • 好发现!为了清楚起见,您想透露防病毒软件的名称吗?
  • @Crypth,它是趋势科技。在过去的 4 年里我不需要重新运行这个测试,所以我不知道它是否还在做。
  • 干杯,一些客户的 HTTP 请求遇到了一些意想不到的问题,我们一直在考虑代理重写流量,但显然 AV 也是一个可能的原因。
【解决方案3】:

MSDN docs 显示设置 TcpClient.NoDelay = true,而不是 TcpClient.Client.NoDelay 属性。你试过吗?

【讨论】:

  • 没有骰子。我过去也成功地使用过它。不过谢谢!
  • 一些想法:您确定服务器接收缓冲区在发送时未满(这会导致发送缓冲区直到被阻塞)?此外 - MSDN 文档显示读取 NoDelay 属性以测试它是否已成功设置 - 尝试添加它以验证客户端/套接字是否满足请求(不知道为什么它不会)。最后,这里有一个关于这个主题的相关 SO 问题:stackoverflow.com/questions/5523565/…
【解决方案4】:

您的测试代码很好(我假设您发送了有效的 HTTP)。您应该检查的是为什么 TCP 服务器在从 TCP 连接读取时表现不佳。 TCP 是一种流协议 - 这意味着您不能对数据包的大小做出任何假设,除非您在数据协议中明确指定这些大小。例如,您可以使用固定大小(2 字节)前缀为所有数据包添加前缀,该前缀将包含要接收的数据的大小。

读取 HTTP 时,通常读取由几个阶段组成:读取 HTTP 行、读取 HTTP 标头、读取 HTTP 内容(如果适用)。前两部分没有任何大小规格,但它们有特殊分隔符 (CRLF)。

Here 是一些如何读取和解析 HTTP 的信息。

【讨论】:

  • 这就是我编写测试代码的原因 - 这样我就可以测试对 Web 服务器的修复。
猜你喜欢
  • 2014-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多