c++ - 为什么winsock 不在这里提供WSAESHUTDOWN?
问题描述
最小的,可重现的例子:
#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
. 如果所有创建的套接字都已交付shutdown
,WSAESHUTDOWN
则程序会干净地退出。
我做错了什么,误解了shutdown
应该如何工作,还是这是一个 Windows 错误?
由于这可能是 Windows 错误,因此我使用的是 Microsoft Windows 版本 21H1(OS Build 19043.1165)。
我希望在我使用套接字时收到WSAESHUTDOWN
,shutdown
并且当我shutdown
使用两个套接字时会发生这种情况,但是如果我使用shutdown
一个套接字然后阻塞线程连接,则程序会挂起。我试图诊断的正是这个问题。
更新:使用的原始问题recv
,但显然这是 UDP 套接字的问题,并且缺少关闭错误发生在recv
或recvfrom
。
解决方案
recv()
除非您首先调用connect()
以将远程对等方与套接字静态关联,否则您不能在 UDP 套接字上使用。否则,recv()
无法返回任何数据报,因为它不知道哪些数据报来自所需的对等方,因此它将丢弃所有数据报。
由于您没有调用connect()
,因此您应该WSAENOTCONN
在两个线程中都遇到错误。
如果您不打算connect()
使用套接字,则需要使用recvfrom()
。