Search code examples
cudpiocp

Binding to a UDP socket for IOCP using C


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).


Solution

  • 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.