我遇到了谷歌搜索似乎无法解决的问题。为了简单起见,我有一个用C#编写的客户端和一个用C编写的Linux服务器。客户端在循环中调用Send(缓冲区)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??
}
看起来发送缓冲区已满并拒绝数据,直到它再次变为空,处于阻塞模式和非阻塞模式。更好的是,我从来没有得到任何例外!?我希望有些例外可以提出,但没有。 :(任何想法?Thnx提前。
答案 0 :(得分:2)
TCP是面向流的。这意味着recv
可以读取1和未完成的字节总数(已发送但尚未读取)之间的任何字节数。 “消息”不存在。可以拆分或合并已发送的缓冲区。
无法从TCP获取消息行为。无法使recv
读取至少N个字节。消息语义由应用程序协议构造。通常,通过使用固定大小的消息或长度前缀。通过执行读循环,您可以读取至少N个字节。
从代码中删除该假设。
答案 1 :(得分:1)
我认为这个问题归因于nagle algorithm:
Nagle算法旨在通过导致减少网络流量 套接字缓冲小数据包,然后组合并发送它们 在某些情况下一包。 TCP数据包由40组成 标头字节加上正在发送的数据。当小数据包时 与TCP一起发送,TCP头部产生的开销可以 成为网络流量的重要组成部分。在重负荷 网络,这种开销导致的拥塞可能导致 丢失数据报和重传,以及过度传播 拥堵造成的时间。 Nagle算法禁止发送 新的传出数据从用户到达时的新TCP段(如果有) 以前在连接上传输的数据仍然未被确认。
调用client.Send函数并不意味着将发送TCP段。 在您的情况下,由于缓冲区很小,naggle算法会将它们重新组合成更大的段。检查服务器端收到的十几个缓冲区是否包含整个数据。
当您添加Thread.Sleep(100)时,您将在服务器端收到100个数据包,因为nagle algotithm不会等待更长时间以获取更多数据。
如果您的应用程序确实需要短延迟,则可以为TcpClient明确禁用nagle算法:将NoDelay属性设置为true。在代码的创建中添加此行:
client.NoDelay = true;
答案 2 :(得分:0)
我很天真地认为TCP堆栈存在问题。这是我的服务器代码。在数据操作之间的某处,我在存储消息的缓冲区上使用了strncpy()函数。每条消息最后都包含\ 0。无论给定的计数(缓冲区长度)如何,Strncpy都只复制缓冲区中的第一条消息(第一个字符串)。这导致我以为我丢失了信息。
当我在客户端上使用send()调用之间的延迟时,消息没有得到缓冲。因此,strncpy()使用一条消息处理缓冲区,一切顺利。这种“现象”导致我们认为发送电话的速度导致了我的问题。
再次感谢您的帮助,您的评论让我感到惊讶。 :)