I am trying to read from a UDP port, from a local (loopback) application, using IOCP. IOCP works fine for TCP/IP, but I am unable to open the socket properly for UDP.
This is what I am doing:
// same as for tcp/ip
struct sockaddr_in remoteAddr = { 0 };
remoteAddr.sin_addr.s_addr = LOOPBACK_ADDRESS;
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_port = htons(portNumber);
// using SOCK_DGRAM here
SOCKET sock = INVALID_SOCKET;
sock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_IP,
NULL, 0, WSA_FLAG_OVERLAPPED);
if( sock == INVALID_SOCKET ) {
LOG("WSASocketW failed: %d", WSAGetLastError());
return;
}
nRet = WSAConnect(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr),
NULL, NULL, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
LOG("WSAConnect failed: %d", WSAGetLastError());
return;
}
nRet = WSARecv(sock, &wsabuf, 1, NULL, &flags, &overlapped, NULL);
if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
{
LOG("WSARecv failed: %d", WSAGetLastError());
return;
}
// no errors of any kind
LOG("seems good so far");
Everything passes without errors, but GetQueuedCompletionStatus
inside the worker loop thread never returns. If I do the same thing to connect to a TCP socket (just replace SOCK_DGRAM
with SOCK_STREAM
basically), I get data inside the loop.
Am I doing something obviously wrong?
(Btw) I know I could use WSARecvFrom
, but I would like to reuse as much code as possible from the TCP socket. I.e. hopefully, set everything up and then post WSARecv
calls inside the worker thread regardless of the type of the socket (WSARecv
is supposed to work with UDP properly, AFAIK).
Managed to get it to work, thanks to the comment by @WouterHuysentruit.
Basically, if I want to receive UDP packets using WSARecv
, I need to bind
. If I want to send UDP packets using WSASend
, I need to connect
. So the following works:
if (port_type == incoming_packets)
{
// bind to port
ret = bind(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr));
...
WSARecv(...);
}
else
{
// this can send to a loopback udp port which is bound in
// a different application
ret = WSAConnect(*sock, (const struct sockaddr*)&remoteAddr, sizeof(remoteAddr), ...);
...
WSASend(...);
}
As others have pointed out, WSAReceiveFrom
/WSASendTo
are usually a better choice for UDP, but in this case I can support multiple port types using IOCP transparently.