UDP: Message Passing

Another kind of client-server setup is one that uses not connections, but messages. UDP communications involve much lower overhead but also provide less reliability, as there are no promises that messages will arrive at all, let alone in order and unmangled. Still, UDP offers some advantages over TCP, including being able to ``broadcast'' or ``multicast'' to a whole bunch of destination hosts at once (usually on your local subnet). If you find yourself overly concerned about reliability and start building checks into your message system, then you probably should use just TCP to start with.

Here's a UDP program similar to the sample Internet TCP client given above. However, instead of checking one host at a time, the UDP version will check many of them asynchronously by simulating a multicast and then using select to do a timed-out wait for I/O. To do something similar with TCP, you'd have to use a different socket handle for each host.

    #!/usr/bin/perl -w
    use strict;
    require 5.002;
    use Socket;
    use Sys::Hostname;

    my ( $count, $hisiaddr, $hispaddr, $histime, 
	 $host, $iaddr, $paddr, $port, $proto, 
	 $rin, $rout, $rtime, $SECS_of_70_YEARS);

    $SECS_of_70_YEARS      = 2208988800;

    $iaddr = gethostbyname(hostname());
    $proto = getprotobyname('udp');
    $port = getservbyname('time', 'udp');
    $paddr = sockaddr_in(0, $iaddr); # 0 means let kernel pick

    socket(SOCKET, PF_INET, SOCK_DGRAM, $proto)   || die "socket: $!";
    bind(SOCKET, $paddr)                          || die "bind: $!";

    $| = 1;
    printf "%-12s %8s %s\n",  "localhost", 0, scalar localtime time;
    $count = 0;
    for $host (@ARGV) {
	$count++;
	$hisiaddr = inet_aton($host) 	|| die "unknown host";
	$hispaddr = sockaddr_in($port, $hisiaddr);
	defined(send(SOCKET, 0, 0, $hispaddr))    || die "send $host: $!";
    }

    $rin = '';
    vec($rin, fileno(SOCKET), 1) = 1;

    # timeout after 10.0 seconds
    while ($count && select($rout = $rin, undef, undef, 10.0)) {
	$rtime = '';
	($hispaddr = recv(SOCKET, $rtime, 4, 0)) 	|| die "recv: $!";
	($port, $hisiaddr) = sockaddr_in($hispaddr);
	$host = gethostbyaddr($hisiaddr, AF_INET);
	$histime = unpack("N", $rtime) - $SECS_of_70_YEARS ;
	printf "%-12s ", $host;
	printf "%8d %s\n", $histime - time, scalar localtime($histime);
	$count--;
    }