【发布时间】:2015-12-03 04:10:08
【问题描述】:
在Windows中,当非阻塞发送返回-1时,WSAGetLastError()返回WSAENOBUFS,而不是WSAEWOULDBLOCK,这与linux不同。
如何让 WSAGetLastError() 返回 WSAEWOULDBLOCK?
【问题讨论】:
标签: windows sockets winapi tcp nonblocking
在Windows中,当非阻塞发送返回-1时,WSAGetLastError()返回WSAENOBUFS,而不是WSAEWOULDBLOCK,这与linux不同。
如何让 WSAGetLastError() 返回 WSAEWOULDBLOCK?
【问题讨论】:
标签: windows sockets winapi tcp nonblocking
这是两种完全不同的错误情况。
WSAEWOULDBLOCK 表示套接字发送缓冲区已满,在不阻塞调用线程的情况下无法接受更多数据。
WSAENOBUFS 表示底层内核缓冲区本身已满,根本无法接受更多数据。这很好地表明您一次向套接字发送了太多数据。在正常情况下,您应该不会收到此错误。
这是来自微软知识库的一个有趣的花絮:
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 之前遇到)时,STOP SENDING DATA 直到套接字准备好接收更多数据。根据您的代码,这可以通过select() 报告套接字是可写的,或者通过您从WSAAsyncSelect()/WSAEventSelect() 接收到FD_WRITE 通知来指示。
【讨论】:
setsockopt(SO_SNDBUF) 来提高套接字发送缓冲区的大小,但您无法控制底层内核的缓冲区大小。