【问题标题】:TCP segments disappearingTCP 段消失
【发布时间】:2013-10-16 20:54:40
【问题描述】:

我遇到了一个谷歌搜索似乎无法解决的问题。为了简单起见,我有一个用 C# 编写的客户端和一个用 C 编写的运行 Linux 的服务器。客户端在循环中调用 Send(buffer) 100 次。问题是服务器只接收到十几个。如果我把一个足够大的睡眠放在一个循环中,一切都会好起来的。缓冲区很小 - 大约 30B。我读到了 Nagle 的算法和 ACK 延迟,但它没有解决我的问题。

          for(int i = 0; i < 100; i++)
          { 
            try
            {                  
                client.Send(oneBuffer, 0, oneBuffer.Length, SocketFlags.None)                    
            }
            catch (SocketException socE)
            {
                if ((socE.SocketErrorCode == SocketError.WouldBlock)
                  || (socE.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
                  || (socE.SocketErrorCode == SocketError.IOPending))
                {
                   Console.WriteLine("Never happens :(");
                }
            }
            Thread.Sleep(100); //problem solver but why??

          }

在阻塞模式和非阻塞模式下,看起来发送缓冲区已满并拒绝数据,直到它再次变空。更好的是,我从来没有例外!?我希望会引发一些异常,但什么也不会。 :( 有什么想法吗?谢谢。

【问题讨论】:

  • 我会先分析 TCP 流量(在 Windows PC 上使用 WireShark,在 Linux 服务器上使用 tcpdump!)。
  • 发生了哪些其他 SocketExceptions?你正在吞咽一些。你如何关闭套接字?使用关机(发送)。显示 Linux 服务器代码。
  • 您在服务器端究竟收到了什么?收到的十几个数据包是否包含全部数据?
  • 让我猜猜:你假设 recv 一次会读取整个“消息”或一个“缓冲区”?
  • @kol 我肯定会这样做

标签: c# tcp


【解决方案1】:

TCP 是面向流的。这意味着recv 可以读取介于 1 和未完成字节总数(已发送但尚未读取)之间的任意字节数。 “消息”不存在。发送的缓冲区可以拆分或合并。

无法从 TCP 获取消息行为。没有办法让recv 至少读取 N 个字节。消息语义由应用协议构建。通常,通过使用固定大小的消息或长度前缀。通过执行读取循环,您可以读取至少 N 个字节。

从您的代码中删除该假设。

【讨论】:

  • 是的,是的,当然。我意识到了这一点。可能是我表达的不够清楚。我从来没有这样的假设。 :) 我很少用英文写东西,所以表达的不是很好,对不起。再次,谢谢帮助。
【解决方案2】:

我认为这个问题是由于nagle algorithm

Nagle 算法旨在通过导致 套接字缓冲小数据包,然后组合并发送它们 在某些情况下一个数据包。一个 TCP 数据包由 40 个 标头字节加上正在发送的数据。当小数据包 与 TCP 一起发送,TCP 标头产生的开销可以 成为网络流量的重要组成部分。在重载时 网络,这种开销导致的拥塞可能导致 丢失数据报和重传,以及过度传播 拥塞造成的时间。 Nagle 算法禁止发送 新的传出数据从用户到达时的新 TCP 段(如果有) 先前在连接上传输的数据仍未得到确认。

调用 client.Send 函数并不意味着将发送 TCP 段。 在您的情况下,由于缓冲区很小,因此 naggle 算法会将它们重新组合成更大的段。在服务器端检查收到的十几个缓冲区是否包含全部数据。

当您添加 Thread.Sleep(100) 时,您将在服务器端收到 100 个数据包,因为 nagle 算法不会等待更多数据。

如果您的应用程序确实需要较短的延迟,您可以为您的 TcpClient 显式禁用 nagle 算法:将 NoDelay 属性设置为 true。在代码的开头添加这一行:

client.NoDelay = true;

【讨论】:

  • 谢谢,但 Nagle 没有问题。在在这里发布问题之前,我做了功课并阅读了我能找到的所有内容,包括 Nagle。 :)
【解决方案3】:

我天真地认为 TCP 堆栈有问题。这是我的服务器代码。在数据操作之间的某个地方,我在存储消息的缓冲区上使用了 strncpy() 函数。每条消息的末尾都包含 \0。 Strncpy 只从缓冲区复制第一条消息(第一个字符串),而不管给出的计数(缓冲区长度)。这导致我认为我丢失了消息。

当我在客户端使用 send() 调用之间的延迟时,消息没有被缓冲。所以,strncpy() 处理了一个带有一条消息的缓冲区,一切都很顺利。这种“现象”让我们认为是发送呼叫的速度导致了我的问题。

再次感谢您的帮助,您的 cmets 让我感到好奇。 :)

【讨论】:

    猜你喜欢
    • 2015-05-07
    • 1970-01-01
    • 1970-01-01
    • 2013-04-22
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    • 2015-09-26
    • 1970-01-01
    相关资源
    最近更新 更多