当非阻塞发送返回-1时,WSAGetLastError()永远不会返回WSAEWOULDBLOCK,为什么?

时间:2015-09-07 14:54:09

标签: windows sockets winapi tcp nonblocking

在Windows中,当nonblock发送返回-1时,WSAGetLastError()返回WSAENOBUFS,而不是WSAEWOULDBLOCK,这与linux不同。

如何让WSAGetLastError()返回WSAEWOULDBLOCK?

1 个答案:

答案 0 :(得分:2)

这是两个完全不同的错误条件。

WSAEWOULDBLOCK表示套接字发送缓冲区已满,无法阻止调用线程,无法接受更多数据。

WSAENOBUFS表示底层内核缓冲区本身已满,无法接受更多数据。这是一个很好的迹象,表明您一次向套接字发送过多数据。在正常情况下,您不应该收到此错误。

以下是微软KB的一个有趣的消息:

Design issues - Sending small data segments over TCP with Winsock

  

为了优化应用程序层的性能, Winsock将数据缓冲区从应用程序发送调用复制到Winsock内核缓冲区。然后,堆栈使用自己的启发式算法(例如Nagle算法)来确定何时实际将数据包放在线路上。您可以使用SO_SNDBUF选项更改分配给套接字的Winsock内核缓冲区的数量(默认情况下为8K)。 如果需要,Winsock可以缓冲的内容远远超过SO_SNDBUF缓冲区大小。在大多数情况下,应用程序中的发送完成仅指示应用程序发送调用中的数据缓冲区被复制到Winsock内核缓冲区,并不表示数据已经到达网络介质。唯一的例外是当您通过将SO_SNDBUF设置为0来禁用Winsock缓冲时。

     

Winsock使用以下规则来指示应用程序的发送完成(取决于调用发送的方式,完成通知可以是从阻塞调用返回的函数,发出事件信号或调用通知函数,等等):

     

•如果套接字仍在SO_SNDBUF配额范围内,Winsock将从应用程序发送中复制数据,并向应用程序指示发送完成。

     

•如果套接字超出SO_SNDBUF配额,并且堆栈内核缓冲区中仍然只有一个先前缓冲的发送,则Winsock将从应用程序发送中复制数据,并向应用程序指示发送完成。

     

如果套接字超出SO_SNDBUF配额且堆栈内核缓冲区中有多个先前缓冲的发送,则Winsock将从应用程序发送中复制数据。 Winsock不指示应用程序的发送完成,直到堆栈完成足够的发送以将套接字放回SO_SNDBUF配额或仅一个未完成的发送条件。

当您尝试发送的数据超过内核可以物理处理的数据时,会报告

WSAENOBUFS

  

WSAENOBUFS
  10055个
  没有缓冲空间。
  无法执行对套接字的操作,因为系统缺少足够的缓冲区空间或者因为队列已满。

您需要将数据分解为更小的块,以免压倒内核。理想情况下,为了获得最佳性能,您不应超过getsockopt(SO_SNDBUF)-1报告的缓冲区大小。但无论哪种方式,当您遇到WSAEWOULDBLOCK(您应该在WSAENOBUFS之前遇到)时,停止发送数据直到套接字准备好接收更多数据。根据您的代码,可以通过select()报告套接字是可写的,或者是您从FD_WRITE / WSAAsyncSelect()收到WSAEventSelect()通知。