简体   繁体   中英

getaddrinfo and INADDR_ANY

Spent a couple of hours searching, still puzzled. From what I've found, INADDR_ANY is meant to specify that the socket will accept connections with any address that is assigned to the server. The following, however, results in the client only being able to connect to localhost:7777 from the same machine.

addrinfo hints;
addrinfo* result;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(INADDR_ANY, "7777", &hints, &result);

SOCKET listenSocket = INVALID_SOCKET;
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);

The only solution that I've found is to change INADDR_ANY to the machine's local IP:

getaddrinfo("192.168.0.105", "7777", &hints, &result);

I need to understand how INADDR_ANY works, because I feel like I'm just misusing it somehow. Any help would be appreciated.

INADDR_ANY has nothing to do with your issue.

The first parameter of getaddrinfo() is a const char * specifying an IP address or hostname. But INADDR_ANY is an integer instead. The only reason your code even compiles is because INADDR_ANY is defined as integer constant 0, which is the only integer constant that is allowed to be assigned to a pointer. So, you are actually passing NULL to the first parameter. Which is fine in this situation, as that is what you need anyway.

What you are not taking into account is that getaddrinfo() returns a linked list of addresses, which may contain multiple addresses, especially if you use AF_UNSPEC . Using that family tells getaddrinfo() that it can return addresses for both IPv4 and IPv6. But you are only using the first address in the list, which just happens to correspond to localhost (127.0.0.1 in IPv4, ::1 in IPv6).

For a server, you should be creating and binding a separate listening socket for each address in the output list. That will let you bind to all of the addresses that match the criteria you passed to getaddrinfo() , eg:

addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;

addrinfo* result;

if (getaddrinfo(NULL, "7777", &hints, &result) == 0)
{
    for (addrinfo *addr = result; addr != NULL; addr = addr->ai_next)
    {
        SOCKET listenSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (listenSocket != INVALID_SOCKET)
        {
            bind(listenSocket, addr->ai_addr, (int)addr->ai_addrlen);
            listen(listenSocket, ...);
            // store listenSocket in a list for later use... 
        }
    }
    freeaddrinfo(result);
}

// use listening sockets as needed... 

Alternatively, as Sam V mentioned in comments, you can skip getaddrinfo() , it is not really helping you in this situation. You could just create and bind 2 sockets directly, one for IPv4 and one for IPv6:

SOCKET listenSocket4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket4 != INVALID_SOCKET)
{
    sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7777);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(listenSocket4, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket4, ...);
}

SOCKET listenSocket6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket6 != INVALID_SOCKET)
{
    sockaddr_in6 addr = {};
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(7777);
    addr.sin6_addr = in6addr_any;

    bind(listenSocket6, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket6, ...);
}

// use listening sockets as needed... 

Or better, create and bind a single dual-stack socket that supports both IPv4 and IPv6:

SOCKET listenSocket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket != INVALID_SOCKET)
{
    BOOL off = FALSE;
    setsockopt(listenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off));

    sockaddr_in6 addr = {};
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(7777);
    addr.sin6_addr = in6addr_any;

    bind(listenSocket, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket, ...);
}

// use listening socket as needed... 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM