Sending to Multiple Hosts One of the nice features of UDP is that the same socket can be used to send to and receive messages from multiple hosts. To illustrate this, let's rewrite the daytime client so that it can ask for the time from multiple hosts. The revised client reads a list of hostnames from the command line and sends a daytime request to each one. It then enters a loop in which it calls recv() repeatedly to read any responses returned by the server. The loop quits when the number of responses received matches the number of requests sent, or until a preset timeout occurs. As it receives each response, the client prints the name of the remote host and the time it returned. Figure 18.3 lists the code for the revised daytime client, udp_daytime_multi.pl . Figure 18.3. This time-of-day client contacts multiple hosts Lines 1 “7: Initialize script We bring in the IO::Socket module and its constants. We again declare the MAX_MSG_LEN constant and define a timeout of 10 seconds for the receipt of all the responses. As before, we set the input record separator to CRLF . Line 8: Set up a signal handler We will use alarm() to set the timeout on received responses, so we install an ALRM signal handler, which simply dies with an appropriate message. Line 9: Create socket We call IO::Socket::INET->new() with a Proto argument of " udp " to create a UDP socket. Because we will specify the destination address within send() and don't want IO::Socket to perform an automatic connect() , we do not provide PeerPort or PeerAddr arguments. Line 10: Look up the daytime port We look up the port number for the UDP version of the daytime service using getservbyname() . Lines 11 “20: Send request to all hosts We now send a request to each host that is given on the command line. For each host we use inet_aton() to translate its name into a packed IP address, and sockaddr_in() to create a suitable destination address. We now send a request to the time-of-day server running on the indicated host. As before, the exact content of the request is irrelevant. If send() reports that the message was successfully queued, we bump up the $host_count counter. Otherwise, we warn about the error. Lines 21 “32: Wait for responses We are now going to wait for up to TIMEOUT seconds for all the responses to come in. If we get all the responses we are expecting, we leave the loop early. We call alarm() to set the timeout and enter a loop that decrements $host_count each time through. Within the body of the loop, we call recv() . If recv() returns false, then an error has occurred and we print the contents of $! and go on to the next iteration of the loop. If recv() succeeds, it places the received message into $daytime . We now attempt to recover the hostname of the sender of the message we just received. Recall that IO::Socket::INET conveniently remembers the peer address from themost recent invocation of recv() . We fetch this address by calling peeraddr() and pass it to gethostbyaddr() to translate it into a DNS name. If gethostbyaddr() fails, we call the socket's peerhost() method to translate the packed peer address into a dotted -quad IP address string. We remove the terminal CRLF from $daytime and print the time and the name of the host that reported it. Line 33: Turn off the alarm On principle, we deactivate the alarm after the loop is done. This isn't strictly necessary because the program will exit immediately anyway. Here is what I saw when I ran the client against several machines located in various parts of the world. Notice that we got a delayed " Connection refused " message from one of the machines, but we can't easily determine which one generated the error (except by a process of elimination ). Finally, notice that the responses don't come back in the same order in which we submitted the requests! % udp_daytime_multi.pl sunsite.auc.dk rtfm.mit.edu wuarchive.wustl.edu prep.ai.mit.edu sent to sunsite.auc.dk... sent to rtfm.mit.edu... sent to wuarchive.wustl.edu... sent to prep.ai.mit.edu... Waiting for responses... PENGUIN-LUST.MIT.EDU: Thu Aug 17 05:57:50 2000 wuarchive.wustl.edu: Thu Aug 17 04:57:52 2000 Connection refused sunsite.auc.dk: Thu Aug 17 11:57:54 2000 Aside from the time-zone differences, the three machines that responded reported the same time, plus or minus a few seconds. It is likely that they are running XNTP servers, a UDP-based protocol for synchronizing clocks with an authoritative source. |