为啥winsock 不在这里提供WSAESHUTDOWN?
Posted
技术标签:
【中文标题】为啥winsock 不在这里提供WSAESHUTDOWN?【英文标题】:Why isn't winsock delivering WSAESHUTDOWN here?为什么winsock 不在这里提供WSAESHUTDOWN? 【发布时间】:2021-11-09 04:20:32 【问题描述】:最小的、可重现的例子:
#include <cassert>
#include <thread>
#include <WinSock2.h>
#pragma comment(lib, "Ws2_32.lib")
constexpr int BUF_SZ = 512;
void RecvThread(SOCKET sock)
int iResult = 0;
char buf[BUF_SZ] = ;
iResult = recv(sock, buf, BUF_SZ, 0);
assert(iResult == SOCKET_ERROR);
assert(WSAGetLastError() == WSAESHUTDOWN);
int main()
int iResult;
WSADATA wsaData;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
assert(iResult == 0);
SOCKET sock_1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
assert(sock_1 != INVALID_SOCKET);
SOCKET sock_2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
assert(sock_2 != INVALID_SOCKET);
in_addr loopback = ;
loopback.S_un.S_un_b.s_b1 = 127;
loopback.S_un.S_un_b.s_b2 = 0;
loopback.S_un.S_un_b.s_b3 = 0;
loopback.S_un.S_un_b.s_b4 = 1;
sockaddr_in addr_1 = ;
addr_1.sin_family = AF_INET;
addr_1.sin_port = 51234;
addr_1.sin_addr = loopback;
iResult = bind(sock_1, (sockaddr const*)&addr_1, sizeof addr_1);
assert(iResult != SOCKET_ERROR);
sockaddr_in addr_2 = ;
addr_2.sin_family = AF_INET;
addr_2.sin_port = 51235;
addr_2.sin_addr = loopback;
iResult = bind(sock_2, (sockaddr const*)&addr_2, sizeof addr_2);
assert(iResult != SOCKET_ERROR);
std::thread t1(RecvThread, sock_1);
std::thread t2(RecvThread, sock_2);
iResult = shutdown(sock_1, SD_BOTH);
assert(iResult != SOCKET_ERROR);
t1.join(); // after shutdown, join the recv thread
iResult = shutdown(sock_2, SD_BOTH);
assert(iResult != SOCKET_ERROR);
t2.join(); // after shutdown, join the recv thread
// note: everything works if we delay t1.join() until after shutdown(sock_2)
iResult = closesocket(sock_1);
assert(iResult != SOCKET_ERROR);
iResult = closesocket(sock_2);
assert(iResult != SOCKET_ERROR);
iResult = WSACleanup();
assert(iResult != SOCKET_ERROR);
return 0;
这在尝试加入在recv
上被阻止的recv 线程时挂起。如果所有创建的套接字都已shutdown
,则传递WSAESHUTDOWN
,程序干净退出。
我是不是做错了什么,误解了 shutdown
应该如何工作,或者这是一个 Windows 错误?
由于这可能是一个 Windows 错误,我使用的是 Microsoft Windows 版本 21H1(操作系统内部版本 19043.1165)。
我希望在我shutdown
套接字时收到WSAESHUTDOWN
,当我shutdown
两个套接字时会发生这种情况,但是如果我shutdown
一个套接字然后阻塞线程连接,程序就会挂起。我正在尝试诊断的正是这个问题。
更新:最初的问题使用了recv
,但显然这是 UDP 套接字的问题,recv
或 recvfrom
都会出现缺少关闭错误。
【问题讨论】:
shutdown()
在 UDP 套接字上不会通过线路发送任何内容,因此它不会影响对等方的 recv()
或 recvfrom()
或他正在阅读的任何内容。它所做的只是阻止您在套接字上再次发送。它不是 TCP,它不提供 FIN。
@user207421 shutdown()
可能不会向对等方发送任何内容(此外,此代码中无论如何都没有分配对等方,因为没有调用connect()
),但它仍然会影响正在调用recv/from()
的套接字。此代码在两个套接字上调用 shutdown()
。
我不确定是否更正在问题中使用recv
的错误(而不是recvfrom
)会改变太多,但我选择更正问题而不是提出新问题几乎重复。使用正确的recv
操作,为什么这里没有提出WAESHUTDOWN
的原始问题。事实上,在调试器中我总是看到第一个套接字被关闭,但不是第二个。
【参考方案1】:
您不能在 UDP 套接字上使用 recv()
,除非您首先调用 connect()
将远程对等方与套接字静态关联。否则,recv()
无法返回任何数据报,因为它不知道哪些数据报来自所需的对等方,因此它将丢弃所有数据。
由于您没有调用connect()
,因此您应该在两个线程中都收到WSAENOTCONN
错误。
如果您不打算使用connect()
套接字,则需要改用recvfrom()
。
【讨论】:
啊,我希望我没有“简化”为recv
; WSAESHUTDOWN
的缺失与 recvfrom
相同。
@CAD97 在这种情况下,代码对我来说看起来不错,并且行为应该按预期运行,所以它一定是 Windows 错误。也就是说,一个简单的解决方案是向套接字的端口发送一个数据包以解除对recvfrom()
的阻塞,或者将WSARecvFrom()
与WSAWaitForMultipleEvents()
一起使用,这样您就可以等待可以在需要时发出信号的辅助事件。
如果您将该花絮添加到您的答案中,我会很乐意将其标记为解决方案。 (评论是暂时的等)以上是关于为啥winsock 不在这里提供WSAESHUTDOWN?的主要内容,如果未能解决你的问题,请参考以下文章