0

我遇到了一个谷歌搜索似乎无法解决的问题。为了简单起见,我有一个用 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??

          }

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

4

3 回答 3

2

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

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

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

于 2013-10-16T12:54:02.887 回答
1

我认为这个问题是由于nagle 算法

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

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

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

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

client.NoDelay = true;
于 2013-10-15T12:37:02.930 回答
0

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

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

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

于 2013-10-16T12:54:40.437 回答