r5ha
r5ha

Reputation: 342

Winsock2 - how to open a TCP socket that allows recv() with MSG_WAITALL?

In this code:

// error checking is omitted

// init Winsock2
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);

// connect to server
struct addrinfo *res = NULL, *ptr = NULL, hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

getaddrinfo(server_ip, "9999", &hints, &res);
SOCKET client_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

connect(client_socket, res->ai_addr, (int)res->ai_addrlen);

freeaddrinfo(res);
res = NULL;

// read the data
unsinged int size1;
if (recv(client_socket, (char*)&size1, sizeof(int), MSG_WAITALL) == SOCKET_ERROR)
{
    return WSAGetLastError();
}

(note the MSG_WAITALL flag in recv()) everything works fine, expect for recv(). WSAGetLastError() returns WSAEOPNOTSUPP.
MSDN states that

Note that if the underlying transport does not support MSG_WAITALL, or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then this call will fail with WSAEOPNOTSUPP. This flag is not supported on datagram sockets or message-oriented sockets.

But it doesn't look like I'm doing something from this list. Why my recv() call doesn't work?

Upvotes: 1

Views: 3848

Answers (2)

Rio Dio
Rio Dio

Reputation: 9

I can assure you that MSTCPIP most certainly supports MSG_WAITALL. You have been misinformed. On Win11 22H2, in the stack trace below which includes both user and kernel mode, notice the last arg to ws2_32!recv is 0x8. That is MSG_WAITALL!

00 fffff807`77b34d83     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : tcpip!TcpTlConnectionReceive

01 fffff807`77b33a9c     : 00000000`00000000 ffffa786`4fb6fba0 00000000`00004020 ffffa786`4f8f31c0 : afd!AfdTLPostUserReceive+0xd3

02 fffff807`77b3358f     : 00000000`00000400 000000b7`438ff778 a7865210`00000000 00000000`00004020 : afd!AfdBReceive+0x2cc

03 fffff807`77b4412d     : ffffa786`4fbdc900 ffffa786`4f8f31c0 00000000`00000000 fffff887`00000400 : afd!AfdReceive+0x30f

04 fffff807`6f2934a5     : ffffa786`4fbdc900 00000000`00000002 00000000`00000001 fffff807`6f26a179 : afd!AfdDispatchDeviceControl+0x7d

05 fffff807`6f6cb210     : ffffa786`4fbdc900 00000000`00000002 ffffa786`4fbdc900 ffffa786`52113990 : nt!IofCallDriver+0x55

06 fffff807`6f6c97dc     : 00000000`00000000 00000000`00012017 fffff887`6a867520 ffffa786`4fbdc900 : nt!IopSynchronousServiceTail+0x1d0

07 fffff807`6f6c7ab6     : 000000b7`438ff620 0000021e`ccceb430 00000000`00000000 00000000`00000000 : nt!IopXxxControlFile+0x72c

08 fffff807`6f433965     : ffffa786`50671080 fffff887`6a867520 000000b7`438ff5e8 000000b7`438ff298 : nt!NtDeviceIoControlFile+0x56

09 00007fff`8234ee54     : 00007fff`7ebd5925 00000000`00000000 00000000`000001f7 0000021e`ccceb430 : nt!KiSystemServiceCopyEnd+0x25

0a 00007fff`7ebd5925     : 00000000`00000000 00000000`000001f7 0000021e`ccceb430 00000000`00000000 : ntdll!NtDeviceIoControlFile+0x14

0b 00007fff`808b2417     : 00000000`000000ec 0000021e`cccd61c0 00000000`00000400 000000b7`438ff758 : mswsock!WSPRecv+0x2e5

0c 00007ff6`b2c0112e     : 00000000`000000ec 00000000`00000000 00007fff`7fabf4a0 00000000`00000008 : WS2_32!recv+0x197

Upvotes: -1

Remy Lebeau
Remy Lebeau

Reputation: 595467

it doesn't look like I'm doing something from this list.

Yes, you are - the very first item on the list:

the underlying transport does not support MSG_WAITALL

Microsoft's default TCP transport provider does not support MSG_WAITALL. recv(), and Winsock in general, is not limited to just Microsoft's TCP provider. It supports 3rd party providers, and any transport protocols that the provider supports - TCP, UDP, IPX, ICMP, RAW, etc.

When using Microsoft's TCP, if you want recv() to wait until all of the requested TCP data has been received, you have to set the socket to blocking mode (its default mode) and then set the flags parameter of recv() to 0. But even that is not guaranteed, recv() can return with fewer bytes than requested, so you should be prepared to call recv() in a loop until all intended bytes have actually been received.

Upvotes: 1

Related Questions