4

The constant INADDR_ANY is the so-called IPv4 wildcard address. The wildcard IP address is useful for applications that bind Internet domain sockets on multihomed hosts. If an application on a multihomed host binds a socket to just one of its host’s IP addresses, then that socket can receive only UDP datagrams or TCP connection requests sent to that IP address. However, we normally want an application on a multihomed host to be able to receive datagrams or connection requests that specify any of the host’s IP addresses, and binding the socket to the wildcard IP address makes this possible.

struct sockaddr_in server_address;
int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_address, 0, sizeof(struct sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY); // here is my quesion
server_address.sin_port = htons(9734);

bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address));

Question>

If we bind the socket to a specific IP address, then the socket can only receive UPD/TCP requests sent sent to that IP address.

As I show in the above code, now the socket server_sockfd is bound with INADDR_ANY. I just feel confused here b/c if the socket can receive any request on the internet, how it can still work well. There are tons of requests of UDP/TCP on internet, if the socket responses to everybody, , how can it still work?

// updated code for client side //

int
main(int argc, char *argv[])
{
    struct sockaddr_in6 svaddr;
    int sfd, j;
    size_t msgLen;
    ssize_t numBytes;
    char resp[BUF_SIZE];

    if (argc < 3 || strcmp(argv[1], "--help") == 0)
        usageErr("%s host-address msg...\n", argv[0]);

    /* Create a datagram socket; send to an address in the IPv6 somain */

    sfd = socket(AF_INET6, SOCK_DGRAM, 0);      /* Create client socket */
    if (sfd == -1)
        errExit("socket");

    memset(&svaddr, 0, sizeof(struct sockaddr_in6));
    svaddr.sin6_family = AF_INET6;
    svaddr.sin6_port = htons(PORT_NUM);
    if (inet_pton(AF_INET6, argv[1], &svaddr.sin6_addr) <= 0)
        fatal("inet_pton failed for address '%s'", argv[1]);

    /* Send messages to server; echo responses on stdout */

    for (j = 2; j < argc; j++) {
        msgLen = strlen(argv[j]);
        if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
                    sizeof(struct sockaddr_in6)) != msgLen)
            fatal("sendto");

        numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, NULL, NULL);
        if (numBytes == -1)
            errExit("recvfrom");

        printf("Response %d: %.*s\n", j - 1, (int) numBytes, resp);
    }

    exit(EXIT_SUCCESS);
}

// updated for server side code

int
main(int argc, char *argv[])
{
    struct sockaddr_in6 svaddr, claddr;
    int sfd, j;
    ssize_t numBytes;
    socklen_t len;
    char buf[BUF_SIZE];
    char claddrStr[INET6_ADDRSTRLEN];

    /* Create a datagram socket bound to an address in the IPv6 somain */

    sfd = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sfd == -1)
        errExit("socket");

    memset(&svaddr, 0, sizeof(struct sockaddr_in6));
    svaddr.sin6_family = AF_INET6;
    svaddr.sin6_addr = in6addr_any;                     /* Wildcard address */
    svaddr.sin6_port = htons(PORT_NUM);

    if (bind(sfd, (struct sockaddr *) &svaddr,
                sizeof(struct sockaddr_in6)) == -1)
        errExit("bind");

    /* Receive messages, convert to uppercase, and return to client */

    for (;;) {
        len = sizeof(struct sockaddr_in6);
        numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
                            (struct sockaddr *) &claddr, &len);
        if (numBytes == -1)
            errExit("recvfrom");

        /* Display address of client that sent the message */

        if (inet_ntop(AF_INET6, &claddr.sin6_addr, claddrStr,
                    INET6_ADDRSTRLEN) == NULL)
            printf("Couldn't convert client address to string\n");
        else
            printf("Server received %ld bytes from (%s, %u)\n",
                    (long) numBytes, claddrStr, ntohs(claddr.sin6_port));

        for (j = 0; j < numBytes; j++)
            buf[j] = toupper((unsigned char) buf[j]);

        if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) !=
                numBytes)
            fatal("sendto");
    }
}

// updated for how to run this server/client programs.

$ ./server_program &
[1] 31047
$ ./client_program ::1 ciao // Send to server on local host
Server received 4 bytes from (::1, 32770)
Response 1: CIAO
q0987
  • 34,938
  • 69
  • 242
  • 387

1 Answers1

12

It doesn't get requests for every IP address on the internet(a), it gets requests for every IP address that it services. For example, it may have multiple NICs, each with a separate IP address or it may have a single NIC capable of managing multiple IP addresses (it may even have multiple NICs, each capable of handling multiple IP addresses.

The key snippet to look at is:

... we normally want an application on a multi-homed host to be able to receive datagrams or connection requests that specify any of the host’s IP addresses (my italics).

In other words, you may have a multi-homed set-up where your machine services 10.0.0.15 and 10.0.0.16. Using INADDR_ANY will allow you to pick up traffic for both those addresses, without picking up requests for 10.0.0.17 which may be the machine on the other end of the bench (or other side of the planet).

The following table, with the top row being request destinations and the left column being the address you're listening on, shows whether you'll be given a request (Y) or not (N):

Request to>  10.0.0.15  10.0.0.16  10.0.0.17
Bind to:    *-------------------------------
10.0.0.15   |    Y          N          N
10.0.0.16   |    N          Y          N
INADDR_ANY  |    Y          Y          N

(a) It doesn't even see the vast majority of requests on the net. The vast majority don't even make it to your nearest router (or probably even your ISP). Even those that do make it to your nearest router, your particular machine might not see if they're destined for another machine on the local segment (promiscuous mode notwithstanding).

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    @q0987 The operating system's network stack won't deliver IP traffic not intended for one of that machine's addresses to programs, unless some sort of diagnostic mode (ie, "packet sniffing") is requested. Quite often appropriate packet addressing at a lower level and/or switched networks will prevent much of the wrong-address traffic from even getting as high as the IP level of the network stack. – Chris Stratton Aug 01 '11 at 04:15
  • Please see the updated client side code. The client side code doesn't specify the server IP address, in your case it doesn't specify any ip in the range from 10.0.0.15 to 10.0.0.16. Then how does the server know that the client sends request to it? – q0987 Aug 01 '11 at 04:15
  • @q0987 it may help you to learn about the networking stack. Every networked machine can have one or more IP addresses. It will only respond to traffic directed to one of those IP addresses. The magic address 0.0.0.0 simply means "any of the IP addresses this machine is responsible for". – Chris Eberle Aug 01 '11 at 04:15
  • @q0987, every IP packet has a source and destination IP address in its header (despite what your client code does). This is something done underneath your client code by the TCP/IP stack. Hence every packet arriving is known to come from a specific source and to a specific destination (one of the possibly many IP addresses this machine is servicing). – paxdiablo Aug 01 '11 at 04:17
  • @paxdiablo, thanks for the nice diagram. You have made me understood part of the question. Now, the another part I still have problems to understand is that why server can get the response from client program which doesn't specify any server IP address? – q0987 Aug 01 '11 at 04:20
  • 1
    @q0987 a socket server cannot get traffic from a client which does not specify an IP address to send to (though there is a broadcast address that can be specified). Similarly, the server does have to specify an address to receive at, but that address can be "any of the addresses of this computer" – Chris Stratton Aug 01 '11 at 04:22
  • @paxdiablo, please see the command used to trigger server/client in my updated post. -- thx – q0987 Aug 01 '11 at 04:26
  • 2
    @q0987, your client code _does_ specify the server address, with `inet_pton` on `argv[1]`. This call takes presentation format like `10.0.0.15` and turns it into a binary network address. In other words, it's passed on the command line. See http://www.kernel.org/doc/man-pages/online/pages/man3/inet_pton.3.html – paxdiablo Aug 01 '11 at 04:28
  • @paxdiablo, The reason that the server can get the client messages even if the client doesn't specify the server IP is because they use localhost as the communication channel. Is that correct? – q0987 Aug 01 '11 at 04:28
  • @paxdiablo, As I listed above, the test run doesn't specify the server ip address, instead it just uses localhost. – q0987 Aug 01 '11 at 04:29
  • 1
    @q0987: not quite. `::1` _is_ an IP address and you do specify it. It's the IPv6 equivalent of the IPv4 127.0.0.1. The loopback address (localhost) is a convenience to mean this current host but it's as valid as any other IP address. It's almost certainly one of the IP addresses that will be serviced by specifying INADDR_ANY. – paxdiablo Aug 01 '11 at 04:31