Juarrow
Juarrow

Reputation: 2414

Do I have to bind a UDP socket in my client program to receive data? (I always get WSAEINVAL)

I am creating a UDP socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP) via Winsock and trying to recvfrom on this socket, but it always returns -1 and I get WSAEINVAL (10022). Why?

When I bind() the port, that does not happen, but I have read that it is very lame to bind the client's socket.

I am sending data to my server, which answers, or at least, tries to.

Inc::STATS CConnection::_RecvData(sockaddr* addr, std::string &strData)
{
    int ret;            // return code
    int len;            // length of the data
    int fromlen;        // sizeof(sockaddr)
    char *buffer;       // will hold the data
    char c;

    //recv length of the message
    fromlen = sizeof(sockaddr);
    ret = recvfrom(m_InSock, &c, 1, 0, addr, &fromlen);
    if(ret != 1)
    {
#ifdef __MYDEBUG__
        std::stringstream ss;
        ss << WSAGetLastError();
        MessageBox(NULL, ss.str().c_str(), "", MB_ICONERROR | MB_OK);
#endif
        return Inc::ERECV;
    }
    ...

This is a working example I wrote a few moments ago, and it works without the call to bind() in the client:

#pragma comment(lib, "Ws2_32.lib")

#define WIN32_LEAN_AND_MEAN

#include <WS2tcpip.h>
#include <Windows.h>
#include <iostream>

using namespace std;

int main()
{
    SOCKET sock;
    addrinfo* pAddr;
    addrinfo hints;
    sockaddr sAddr;
    int fromlen;
    const char czPort[] = "12345";
    const char czAddy[] = "some ip";

    WSADATA wsa;
    unsigned short usWSAVersion = MAKEWORD(2,2);

    char Buffer[22] = "TESTTESTTESTTESTTEST5";
    int ret;

    //Start WSA
    WSAStartup(usWSAVersion, &wsa);

    //Create Socket
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    //Resolve host address
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_protocol = IPPROTO_UDP;
    hints.ai_socktype = SOCK_DGRAM;

    if(getaddrinfo(czAddy, czPort, &hints, &pAddr))
    {
        std::cerr << "Could not resolve address...\n";
        std::cin.get();
        return 1;
    }

    //Start Transmission
    while(1)
    {
        ret = sendto(sock, Buffer, sizeof(Buffer), 0, pAddr->ai_addr,
                     pAddr->ai_addrlen);
        if(ret != sizeof(Buffer))
        {
            std::cerr << "Could not send data\n";
            std::cin.get();
            return 1;
        }

        fromlen = sizeof(SOCKADDR);
        ret = recvfrom(sock, Buffer, sizeof(Buffer), 0, &sAddr, &fromlen);
        if(ret != sizeof(Buffer))
        {
            std::cout << "Could not receive data -  error: " << 
                          WSAGetLastError() << std::endl;
            std::cin.get();
            return 1;
        }

        Buffer[ret-1] = '\0';
        std::cout << "Received: " << Buffer << std::endl;
    }
    return 0;
}

Upvotes: 42

Views: 99676

Answers (5)

maxlovic
maxlovic

Reputation: 67

If you want your socket to be bound to some local port selected automatically by a system, you can call bind(sock, &sa, sa_len) with a zeroed sockaddr. Just set the family you need. It would default-bind a socket to some port selected by the system.

sockaddr_in sa = {};
sa.sin_family = AF_INET;
const int res = ::bind(socket_id, (sockaddr*) &sa, sizeof sa);
if (res < 0)
{
    std::cerr << "UDP binding has failed\n";
    return;
}

Upvotes: -2

Mecki
Mecki

Reputation: 133199

Your other code sample works because you are using sendto before recvfrom. If a UDP socket is unbound and either sendto or connect are called on it, the system will automatically bind it for you and thus the recvfrom call later on will succeed. recvfrom will not bind a socket, as this call expects the socket to have been bound already, or an error will be thrown.

Upvotes: 57

user925861
user925861

Reputation:

I had the same problem a few weeks ago, the following remarks helped me to understand whether an explicit bind() call is necessary:

recvfrom function (MSDN)

Explicit binding is discouraged for client applications. For client applications using this function, the socket can become bound implicitly to a local address through sendto, WSASendTo, or WSAJoinLeaf.

sendto function (MSDN)

Note If a socket is opened, a setsockopt call is made, and then a sendto call is made, Windows Sockets performs an implicit bind function call. If the socket is unbound, unique values are assigned to the local association by the system, and the socket is then marked as bound.

Upvotes: 6

Warren Young
Warren Young

Reputation: 42383

With UDP, you have to bind() the socket in the client because UDP is connectionless, so there is no other way for the stack to know which program to deliver datagrams to for a particular port.

If you could recvfrom() without bind(), you'd essentially be asking the stack to give your program all UDP datagrams sent to that computer. Since the stack delivers datagrams to only one program, this would break DNS, Windows' Network Neighborhood, network time sync....

You may have read somewhere on the net that binding in a client is lame, but that advice only applies to TCP connections.

Upvotes: 62

Eugen Constantin Dinca
Eugen Constantin Dinca

Reputation: 9150

Here it says the following:

Parameters

s [in]: A descriptor identifying a bound socket.

...

Return Value

WSAEINVAL: The socket has not been bound with bind, or an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, or (for byte stream-style sockets only) len was zero or negative.

As far as I remember bind is not required for a UDP socket because a bind call is made for you by the stack. I guess it's a Windows thing to require a bind on a socket used in a recvfrom call.

Upvotes: 1

Related Questions