linux multicast join doesn't, why? - linux

I need to receive multicast data from two sources on one ethernet connection, 224.0.31.132 port 14384 and 224.0.31.130 port 14382.
First I tried to bind with port 14384 and then join both ip addresses, but I only get the data from 224.0.31.132.
If I bind with 14382 and join both, I only get the data from 224.0.31.130.
So then I tried to create two sockets, bind the first to 14384 and join 224.0.31.132, then bind the second to 14382 and join 224.0.31.130.
When I do that, I get the data from 224.0.31.130, but not from 224.0.31.132, and ip maddr show shows that the join to 224.0.31.132 has apparently been deleted by the setting up of the second socket.
What do I do to receive data from these two sources on the single eth port?
Here is code for first approach:
/* Create a datagram socket on which to receive. */
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
cout<<"Opening datagram socket....OK."<<endl;
/* Enable SO_REUSEADDR to allow other */
/* applications to receive copies of the multicast datagrams. */
{
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Setting SO_REUSEADDR error");
close(sd);
exit(1);
}
else
cout<<"Setting SO_REUSEADDR...OK."<<endl;
}
/* Enable SO_REUSEPORT to allow multiple uses of receive port */
{
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Setting SO_REUSEPORT error");
close(sd);
exit(1);
}
else
cout<<"Setting SO_REUSEPORT...OK."<<endl;
}
/* Bind to the proper port number with the IP address */
/* specified as INADDR_ANY. */
struct sockaddr_in localSock;
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(14384);
localSock.sin_addr.s_addr = INADDR_ANY;
if(bind(sd, (struct sockaddr*)&localSock, sizeof(localSock)))
{
perror("Binding datagram socket error");
close(sd);
exit(1);
}
else
cout<<"Binding datagram socket...OK."<<endl;
/* Join the multicast groups on the local nic interface. */
struct ip_mreq group;
group.imr_interface.s_addr = inet_addr("0.0.0.0");
int ipcount = stoi(getSetup("mcstcount"));
while(ipcount>0)
{
string which = "listenip" + to_string(ipcount);
cout<<"listen to "<<getSetup(which.c_str())<<endl;
group.imr_multiaddr.s_addr = inet_addr(getSetup(which.c_str()).c_str());
ipcount--;
if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
perror("Adding multicast group error");
close(sd);
exit(1);
}
else
cout<<"Adding multicast group...OK."<<endl;
}
// process Mdp feed
while( !stop_cond )
{
/* Read from the socket. */
const int DBUFSZ = 5000;
uint8_t databuf[DBUFSZ];
int datalen;
datalen=read(sd, databuf, DBUFSZ);
if(datalen < 0)
{
perror("Reading datagram message error");
close(sd);
exit(1);
}
//process data
}
Here is resulting output:
Opening datagram socket....OK.
Setting SO_REUSEADDR...OK.
Setting SO_REUSEPORT...OK.
Binding datagram socket...OK.
listen to 224.0.33.80
Adding multicast group...OK.
listen to 224.0.31.130
Adding multicast group...OK.
listen to 224.0.31.132
Adding multicast group...OK.
But data only comes in from 224.0.31.132.

Concluded one cannot use a single socket as there are multiple ports to receive from. Went back to approach two, using multiple sockets. Got it working, not sure what was wrong the first time.

Related

multicast: Linux *must not* bind socket to a specific # but Windows *must*

I have a mdns service discovery that uses the following code for initialization
void mdnssd_init(struct in_addr host, bool compliant) {
int sock;
int res;
struct ip_mreq mreq;
struct sockaddr_in addr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
char param = 32;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*) &param, sizeof(param));
int enable = 1
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &enable, sizeof(enable));
param = 1;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (void*) &param, sizeof(param));
#ifndef _WIN32
if (compliant) {
enable = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void*)&enable, sizeof(enable));
}
#endif
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
/*
* Sending *from * 5353 indicates that we do compliant mDNS query. If we chose
* random ports, the ttl will be much shorter.
*/
if (compliant) addr.sin_port = htons(MDNS_PORT);
// Windows must bind this socket to a specific address, others must not (it's *must*)
#ifdef _WIN32
addr.sin_addr.s_addr = host.s_addr;
#else
addr.sin_addr.s_addr = INADDR_ANY;
#endif
socklen_t addrlen = sizeof(addr);
res = bind(sock, (struct sockaddr *) &addr, addrlen);
if (res < 0) return;
// set outgoing interface for multicast (it's optional, INADDR_ANY could be used)
setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void*) &host.s_addr, sizeof(host.s_addr));
// set multicast groups we are interested by to receive such packets
memset(&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(MDNS_MULTICAST_ADDRESS);
mreq.imr_interface.s_addr = host.s_addr; // optional, INADDR_ANY can be used
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &mreq, sizeof(mreq));
}
It works on all platforms (Linux, Windows, Solaris, FreeBSD, MacOS) but I really don't understand why I need that binding difference between Linux and Windows.
On Linux, if I bind the socket to the address I want to use to send/receive, multicast traffic is not received. Note that queries that require unicast response are properly answered. I understand that some settings here are optional and INADDR_ANY can be used, letting the OS select what interface to send request and receive response (and it works).
On the contrary, on Windows, if the socket is not bound to a specific address, but set to INADDR_ANY, then no multicast traffic is received. Same, queries requiring unicast responses are received.
So it's very puzzling to me that not both options work. I should be able to bind the socket to the address that will be used for sending/receiving - or not. It should work either way, no?

Can I (IGMP) join a stream on two NICs and answer (IGMP) queries on both NICs in Linux?

I made a Linux application to receive multicast traffic. It works when I connect to one interface. When I connect to a stream, in Wireshark, I see an IGMP join, and when the switch sends IGMP queries, Linux replies with an IGMP report for the stream.
However, I need more bandwidth than my one interface can provide. To have more bandwidth, I have multiple interfaces on the same network. I therefore duplicated my code to have two interfaces connect to a stream. In that case, in Wireshark, I see an IGMP join on both interfaces, but when the switch sends IGMP queries, Linux only replies with an IGMP report on one interface. Therefore, the switch timeout occurs and I lose the stream on the interface that is not reporting.
Here is a reproducible example. It doesn't receive any data, but it is enough to see the problem happen in Wireshark:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Create and fill internet socket address structure for both sockets.
struct sockaddr_in internetSocketAdressStructure;
bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
internetSocketAdressStructure.sin_family = AF_INET;
internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
internetSocketAdressStructure.sin_port = htons(10000);
// Create first socket.
int firstSocketToUse;
if ((firstSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("First socket failed");
exit(1);
}
// Set first socket for address reuse.
int reuseAddressForFirstSocket;
reuseAddressForFirstSocket = 1;
if (setsockopt(firstSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForFirstSocket, sizeof(reuseAddressForFirstSocket) ) == -1 ) {
perror("Error setting first socket for address reuse");
exit(1);
}
// Bind first socket.
if (bind(firstSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("First bind failed");
exit(1);
}
// Join stream on first socket.
struct ip_mreq multicastRequestOnFirstInterface;
multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
if (setsockopt(firstSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1) {
perror("Error joining multicast group on Interface 1");
exit(1);
}
// Create second socket.
int secondSocketToUse;
if ((secondSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Second socket failed");
exit(1);
}
// Set second socket for address reuse.
int reuseAddressForSecondSocket;
reuseAddressForSecondSocket = 1;
if (setsockopt(secondSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForSecondSocket, sizeof(reuseAddressForSecondSocket) ) == -1 ) {
perror("Error setting second socket for address reuse");
exit(1);
}
// Bind second socket.
if (bind(secondSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("Second bind failed");
exit(1);
}
// Join stream on second socket.
struct ip_mreq multicastRequestOnSecondInterface;
multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
if (setsockopt(secondSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1) {
perror("Error joining multicast group on Interface 2");
exit(1);
}
// Wait forever.
while(1) {}
}
I saw a post on Stackoverflow suggesting to do this with only one socket, so I tried that, but the same issue occurs. Here is the code for that:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Create and fill internet socket address structure for the socket.
struct sockaddr_in internetSocketAdressStructure;
bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
internetSocketAdressStructure.sin_family = AF_INET;
internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
internetSocketAdressStructure.sin_port = htons(10000);
// Create socket.
int socketToUse;
if ((socketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket failed");
exit(1);
}
// Bind socket.
if (bind(socketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("bind failed");
exit(1);
}
// Set first socket for address reuse.
int reuseAddress;
reuseAddress = 1;
if (setsockopt(socketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddress, sizeof(int) ) == -1 ) {
perror("Error setting socket for address reuse");
exit(1);
}
// Join stream on first interface.
struct ip_mreq multicastRequestOnFirstInterface;
multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1)
{
perror("Error joining multicast group on first interface");
exit(1);
}
// Join stream on second interface.
struct ip_mreq multicastRequestOnSecondInterface;
multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1)
{
perror("Error joining multicast group on second interface");
exit(1);
}
// Wait forever.
while(1) {}
}
I've also tried to put the interfaces in promiscuous mode:
sudo ip link set interface1 promisc on
sudo ip link set interface2 promisc on
and add the noprefixroute option to each interface:
sudo ip addr change 25.25.40.134 dev interface1 noprefixroute
sudo ip addr change 25.25.40.116 dev interface2 noprefixroute
Both those things failed to solve my problem.
That being said, I found sources (1, 2) that might indicate that what I'm trying to do is impossible, though these sources seem somewhat old.
I was able to fix the issue by putting both ports on the switch to different vlans, though that is a clunky/awful solution that might not be acceptable.
Is connecting to multicast streams on the same network through two interfaces and configure the network to answer the IGMP queries on both interfaces possible?
It is explained as clear as day here:
https://access.redhat.com/solutions/53031
And less clearly here:
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
Considering a computer with 2 net interfaces, interfaceA and interfaceB.
Considering that Linux decides to use interfaceB to send packets to ip address X.
Considering a packet that is received on interfaceA from ip address X.
Linux will drop the packet.
Unless you run
sysctl net.ipv4.conf.all.rp_filter=2 in a terminal or add that line to /etc/sysctl.conf.
It enables receiving packets from an ip address on other interfaces than the one it uses to send packets to that ip address!

why my TCP server code send a SYN/ACK on only first packet or only on the first connection?

SOCKET sock;
SOCKET fd;
uint16 port = 18001;
void CreateSocket()
{
struct sockaddr_in server, client; // creating a socket address structure: structure contains ip address and port number
WORD wVersionRequested;
WSADATA wsaData;
int len;
printf("Initializing Winsock\n");
wVersionRequested = MAKEWORD (2, 2);
iResult = WSAStartup (wVersionRequested, &wsaData);
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
// create socket
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
printf("Could not Create Socket\n");
//return 0;
}
printf("Socket Created\n");
// create socket address of the server
memset( &server, 0, sizeof(server));
// IPv4 - connection
server.sin_family = AF_INET;
// accept connections from any ip adress
server.sin_addr.s_addr = htonl(INADDR_ANY);
// set port
server.sin_port = htons(18001);
//Binding between the socket and ip address
if(bind (sock, (struct sockaddr *) &server, sizeof(server)) < 0)
{
printf("Bind failed with error code: %d", WSAGetLastError());
}
//Listen to incoming connections
if(listen(sock,3) == -1){
printf("Listen failed with error code: %d", WSAGetLastError());
}
printf("Server has been successfully set up - Waiting for incoming connections");
for(;;){
len = sizeof(client);
fd = accept(sock, (struct sockaddr*) &client, &len);
if (fd < 0){
printf("Accept failed");
close(sock);
}
//echo(fd);
printf("\n Process incoming connection from (%s , %d)", inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//closesocket(fd);
}
}
The server code is accepting a connection from the client via the ip address and the port number. It is sending SYN/ACK to the client only during the first connection and It is sending like below for the second time: RST / ACK (it is resetting during the second time).
Could anyone tell me what is the error in the above code ??
Look at Accept multiple subsequent connections to socket
Here is a quote: "To service multiple clients, you need to avoid blocking I/O -- i.e., you can't just read from the socket and block until data comes in."

Linux C Socket: Blocked on recv call

In my application i have created a thread for a simple http server, then from within my application i tried to connect to http server but control is blocked/hanged on recv call.
But if try to connect to my application's http server using linux GET command, I will be connected to http server successfully.
As per my understanding by searching the google i found that this is not the right approach.
But if i want to do this, in what should i create the sockets so that i can connect o my http server from within the application.
Below is how my http server socket created
pthread_create(&pt_server, NULL, http_srvr, NULL);
//http server handler
void *http_server()
{
int sockfd, new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
socklen_t sin_size;
struct sigaction sa;
int yes=1;
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(HTTP_PORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
perror("bind");
exit(1);
}
printf("Listening to sockets\n");
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1)
{
perror("accept");
continue;
}
printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
handle_connection(new_fd);
}
}
And following is how i am doing http POST to my http server
/* create socket */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return ERRSOCK;
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
/* connect to server */
if (connect(s, &server, sizeof(server)) < 0)
ret=ERRCONN;
else {
if (pfd) *pfd=s;
/* create header */
if (proxy) {
sprintf(header,
"%s http://%.128s:%d/%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
http_server1,
http_port,
url,
http_user_agent,
additional_header
);
} else {
sprintf(header,
"%s /%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
url,
http_user_agent,
additional_header
);
}
hlg=strlen(header);
/* send header */
if (send(s,header,hlg,0)!=hlg)
ret= ERRWRHD;
/* send data */
else if (length && data && (send(s,data,length,0)!=length) )
ret= ERRWRDT;
else {
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
and following are the contents of http_read_line, and in this function recv call blocked
static int http_read_line (fd,buffer,max)
int fd; /* file descriptor to read from */
char *buffer; /* placeholder for data */
int max; /* max number of bytes to read */
{ /* not efficient on long lines (multiple unbuffered 1 char reads) */
int n=0;
while (n<max) {
if (recv(fd,buffer,1,0)!=1) {
n= -n;
break;
}
n++;
if (*buffer=='\015') continue; /* ignore CR */
if (*buffer=='\012') break; /* LF is the separator */
buffer++;
}
*buffer=0;
return n;
}
You need to either send an HTTP 1.0 header, or else read about content-length in HTTP 1.1. You are reading the stream to EOS when the server is under no obligation to close the connection, so you block. The Content-Length header tells you how much data is in the body: you should only try to read that many bytes.
If you specify HTTP 1.0 (and no fancy headers) the server will close the connection after sending the response.
You have told "In my application i have created a thread for a simple http server, then from within my application
i tried to connect to http server but control is blocked/hanged on recv call."
That means the recv is never returning 0. Now when the recv function will
return a 0? ->When it gets a TCP FIN segment. It seems that your server is never
sending a TCP FIN segment to the client.
The reason that is most likely here is that, your client code needs modification.
You are sending data from from the client, but you are never sending the FIN,
so I assume that your server function is continuing forever and it had not
sent the FIN. This made the recv wait for ever.
In the current code perhaps the fix is to add a line
else {
/*Send the FIN segment, but we can still read the socket*/
shutdown(s, SHUT_WR);
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
In this case the shutdown function sends the TCP FIN and the server function can return and possibly then it would do a proper close.
And on a proper close, the FIN from the server will be received by the client. This would make the recv return 0, instead of getting blocked for ever.
Now if you want to continue any further data transfer from the client, you need to again connect or may be you need to have some different algorithm.
I hope my explanation may help fix the current problem.

Question about INADDR_ANY

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

Resources