为什么调用WSASocket()而不是socket()会导致发送阻塞?

3
我有两个简单的TCPConnection和TCPListener包装类。使用这些类的应用程序在Windows 10上运行,执行以下操作:
线程1 调用getaddrinfo、socket、bind、listen、accept并阻塞等待连接 当连接建立时,客户端套接字传递给第二个线程进行发送 开始阻塞recv()循环
线程2 等待排队发送数据 当队列中有可用数据时,调用send()
当使用socket()调用创建服务器套接字时,这完全正常。但是,当我切换到WSASocket()调用(非重叠)时,如果存在一个挂起的recv()或WSARecv()调用,则send调用(无论是send()还是WSASend())都会阻塞。
我不知道可能导致这种情况。我一直认为从不同线程的同一套接字上发送和接收是可以的。
在posix与WSA变体之间的实现是否有差异?有什么想法可以解释这种行为吗?
调用差异如下:
sock = socket(AF_INET, SOCK_STREAM, IPOROTO_TCP);
sock = WSASocketW(AF_INET, SOCK_STREAM, IPOROTO_TCP, nullptr, 0, 0);

连接的客户端来自Python 3.8.5。


我能想到的唯一可能是TCP协议栈不同,而Python端点报告窗口为空,因此发送被阻塞。但是我还没有验证过,这似乎极不可能... - Jaime
Wireshark显示数据未被发送,这表明发送操作实际上被某种内核对象阻塞。窗口似乎足够大。 - Jaime
1个回答

4
当使用WSASocket()时,需要传递WSA_FLAG_OVERLAPPED标志才能获得与调用socket()相同的行为。如果没有它,Windows将阻止您的WSASend()send()调用,如果WSARecv()recv()在另一个线程中被阻塞。
我以为如果我传递了WSA_FLAG_OVERLAPPED标志,所有套接字操作都会重叠(即所有对WSARecv()WSASend()的调用都需要一个OVERLAPPED结构),但这并不是事实。
正确用法:
sock = WSASocketW(AF_INET, SOCK_STREAM, IPOROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
WSABUF buf { bufferLen, bufferPtr};
DWORD bytes = 0;
DWORD flags = 0;
WSARecv(sock, &buf, 1, &bytes, &flags, nullptr, nullptr); // Thread 1
WSASend(sock, &buf, 1, &bytes, 0, nullptr, nullptr);      //Thread 2

与执行以下操作相同:

sock = socket(AF_INET, SOCK_STREAM, IPOROTO_TCP);
recv(sock, bufferPtr, bufferLen, 0); // Thread 1
send(sock, bufferPtr, bufferLen); // Thread 2

在Windows上,您无法访问OVERLAPPED特性。

1
Jamie,目前MSDN文档中socket()调用的说明是“创建的套接字将具有默认的重叠属性。”因此,预计要使用WSA_FLAG_OVERLAPPED标志创建一个功能等效的套接字。然而,我同意如果WSA_FLAG_OVERLAPPED未设置,则WSASend()在WSARecv调用上阻塞有点奇怪和意外。我之前没有意识到这一点,所以今天早上我学到了新东西,谢谢。 - Len Holgate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接