在 Windows 上,WSASend 失败并显示 WSAENOBUFS

Atu*_*tul 2 sockets windows networking tcp

在 Windows XP 上,当我在非阻塞套接字上迭代调用WSASend时,它会失败并显示 WSAENOBUFS。

我这里有两个案例:

情况1:

在非阻塞套接字上,我调用WSASend。这是伪代码:

while(1)
{
    result = WSASend(...); // Buffersize 1024 bytes
    if (result == -1)
    {
        if (WSAGetLastError() == WSAENOBUFS)
        {
            // Wait for some time before calling WSASend again
            Sleep(1000);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,WSASend 成功返回约 88000 次。然后,它会因 WSAENOBUFS 失败,并且即使在一段时间后尝试也不会恢复,如代码所示。

案例2:

为了解决这个问题,我引用了这个并按照那里的建议,就在上面的代码之前,我用 SO_SNDBUF 调用了setsockopt并设置了 buffersize 0(零)

在这种情况下,WSASend 成功返回约 2600 次。然后就失败了。但等了2600次又成功了又失败。

现在我在这两种情况下都有这些问题:

情况1:

  1. 这里的88000这个数字是由什么因素决定的呢?
  2. 如果失败是因为 TCP 缓冲区已满,为什么一段时间后没有恢复?

案例2:

  1. 再说一遍,这里的2600这个数字是由什么因素决定的呢?
  2. 正如 Microsoft 知识库文章中所述,如果直接从应用程序缓冲区发送而不是内部 TCP 缓冲区,为什么会因 WSAENOBUFS 失败?

编辑:

对于异步套接字(在 Windows XP 上),行为更加奇怪。如果我忽略 WSAENOBUFS 并继续进一步写入套接字,我最终会断开连接 WSAECONNRESET。目前不确定为什么会发生这种情况?

Len*_*ate 5

这些值没有记录,取决于您的计算机上安装的可能位于应用程序和网络驱动程序之间的内容。它们可能与机器中的内存量有关。在 Vista 及更高版本上,限制(很可能是非分页池内存和 I/O 页面锁定限制)可能要高得多。

处理该问题的最佳方法是将应用程序级流量控制添加到您的协议中,这样您就不会认为您可以按照您想要的任何速率发送。请参阅这篇博客文章,详细了解非阻塞和异步 I/O 如何导致资源使用激增,以及除非您有自己的流量控制,否则您如何无法控制它。

总之,永远不要假设您可以使用非阻塞/异步 API 以尽可能快的速度将数据写入线路。请记住,由于 TCP/IP 内部流量控制的工作方式,您可能会使用数量无法控制的本地计算机资源,并且客户端是唯一可以控制这些资源释放回服务器上的操作系统的速度的东西机器。