[ Team LiB ] Previous Section Next Section

21.10 Sending and Receiving

The IP multicast infrastructure session announcement program in the previous section only received multicast datagrams. We will now develop a simple program that sends and receives multicast datagrams. Our program consists of two parts. The first part sends a multicast datagram to a specific group every five seconds and the datagram contains the sender's hostname and process ID. The second part is an infinite loop that joins the multicast group to which the first part is sending and prints every received datagram (containing the hostname and process ID of the sender). This allows us to start the program on multiple hosts on a LAN and easily see which host is receiving datagrams from which senders.

Figure 21.17 shows the main function for our program.

Figure 21.17 Create sockets, fork, and start sender and receiver.

mcast/main.c

 1 #include    "unp.h"

 2 void    recv_all(int, socklen_t);
 3 void    send_all(int, SA *, socklen_t);

 4 int
 5 main(int argc, char **argv)
 6 {
 7     int     sendfd, recvfd;
 8     const int on = 1;
 9     socklen_t salen;
10     struct sockaddr *sasend, *sarecv;

11     if (argc != 3)
12         err_quit("usage: sendrecv <IP-multicast-address> <port#>");

13     sendfd = Udp_client(argv[1], argv[2], (void **) &sasend, &salen);

14     recvfd = Socket(sasend->sa_family, SOCK_DGRAM, 0);

15     Setsockopt(recvfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

16     sarecv = Malloc(salen);
17     memcpy(sarecv, sasend, salen);
18     Bind(recvfd, sarecv, salen);

19     Mcast_join(recvfd, sasend, salen, NULL, 0);
20     Mcast_set_loop(sendfd, 0);

21     if (Fork() == 0)
22         recv_all(recvfd, salen);    /* child -> receives */

23     send_all(sendfd, sasend, salen);    /* parent -> sends */
24 }

We create two sockets, one for sending and one for receiving. We want the receiving socket to bind the multicast group and port, say 239.255.1.2 port 8888. (Recall that we could just bind the wildcard IP address and port 8888, but binding the multicast address prevents the socket from receiving any other datagrams that might arrive destined for port 8888.) We then want the receiving socket to join the multicast group. The sending socket will send datagrams to this same multicast address and port, say 239.255.1.2 port 8888. But if we try to use a single socket for sending and receiving, the source protocol address is 239.255.1.2:8888 from the bind (using netstat notation) and the destination protocol address for the sendto is also 239.255.1.2:8888. However, now the source protocol address that is bound to the socket becomes the source IP address of the UDP datagram, and RFC 1122 [Braden 1989] forbids an IP datagram from having a source IP address that is a multicast address or a broadcast address (see Exercise 21.2 also). Therefore, we must create two sockets: one for sending and one for receiving.

Create sending socket

13 Our udp_client function creates the sending socket, processing the two command-line arguments that specify the multicast address and port number. This function also returns a socket address structure that is ready for calls to sendto along with the length of this socket address structure.

Create receiving socket and bind multicast address and port

1418 We create the receiving socket using the same address family that was used for the sending socket. We set the SO_REUSEADDR socket option to allow multiple instances of this program to run at the same time on a host. We then allocate room for a socket address structure for this socket, copy its contents from the sending socket address structure (whose address and port were taken from the command-line arguments), and bind the multicast address and port to the receiving socket.

Join multicast group and turn off loopback

1920 We call our mcast_join function to join the multicast group on the receiving socket and our mcast_set_loop function to disable the loopback feature on the sending socket. For the join, we specify the interface name as a null pointer and the interface index as 0, telling the kernel to choose the interface.

fork and call appropriate functions

2123 We fork and then the child is the receive loop and the parent is the send loop.

Our send_all function, which sends one multicast datagram every five seconds, is shown in Figure 21.18. The main function passes as arguments the socket descriptor, a pointer to a socket address structure containing the multicast destination and port, and the structure's length.

Obtain hostname and form datagram contents

911 We obtain the hostname from the uname function and build the output line containing it and the process ID.

Send datagram, then go to sleep

1215 We send a datagram and then sleep for five seconds.

The recv_all function, which is the infinite receive loop, is shown in Figure 21.19.

Allocate socket address structure

9 A socket address structure is allocated to receive the sender's protocol address for each call to recvfrom.

Read and print datagrams

1015 Each datagram is read by recvfrom, null-terminated, and printed.

Figure 21.18 Send a multicast datagram every five seconds.

mcast/send.c

 1 #include    "unp.h"
 2 #include    <sys/utsname.h>

 3 #define SENDRATE     5           /* send one datagram every five seconds */

 4 void
 5 send_all(int sendfd, SA *sadest, socklen_t salen)
 6 {
 7     char    line[MAXLINE];      /* hostname and process ID */
 8     struct utsname myname;

 9     if (uname(&myname) < 0)
10         err_sys("uname error");;
11     snprintf(line, sizeof(line), "%s, %d\n", myname.nodename, getpid());

12     for ( ; ; ) {
13         Sendto(sendfd, line, strlen(line), 0, sadest, salen);

14         sleep(SENDRATE);
15     }
16 }
Figure 21.19 Receive all multicast datagrams for a group we have joined.

mcast/recv.c

 1 #include    "unp.h"

 2 void
 3 recv_all(int recvfd, socklen_t salen)
 4 {
 5     int     n;
 6     char    line[MAXLINE + 1];
 7     socklen_t len;
 8     struct sockaddr *safrom;

 9     safrom = Malloc(salen);

10     for ( ; ; ) {
11         len = salen;
12         n = Recvfrom(recvfd, line, MAXLINE, 0, safrom, &len);

13         line[n] = 0;            /* null terminate */
14         printf("from %s: %s", Sock_ntop(safrom, len), line);
15     }
16 }

Example

We run this program on our two systems, freebsd4 and macosx. We see that each system sees the packets that the other is sending.

freebsd4 % sendrecv 239.255.1.2 8888
from 172.24.37.78:51297: macosx, 21891
from 172.24.37.78:51297: macosx, 21891
from 172.24.37.78:51297: macosx, 21891
from 172.24.37.78:51297: macosx, 21891

macosx % sendrecv 239.255.1.2 8888
from 172.24.37.94.1215: freebsd4, 55372
from 172.24.37.94.1215: freebsd4, 55372
from 172.24.37.94.1215: freebsd4, 55372
from 172.24.37.94.1215: freebsd4, 55372
    [ Team LiB ] Previous Section Next Section