【发布时间】: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的原始问题。事实上,在调试器中我总是看到第一个套接字被关闭,但不是第二个。
标签: c++ windows sockets shutdown winsock2