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
14鈥?8
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
19鈥?0
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
21鈥?3
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
9鈥?1
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
12鈥?5
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
10鈥?5
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
|