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.
Note that UDP datagrams are not a bytestream and should not be treated as such. This
makes using I/O mechanisms with internal buffering like stdio (i.e. print() and friends)
especially cumbersome. Use syswrite(), or better send(), like in the example below.
Here's a UDP program similar to the sample Internet TCP client given earlier. 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;
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--;
}
|
|
Note that this example does not include any retries and may consequently fail to contact a
reachable host. The most prominent reason for this is congestion of the queues on the sending
host if the number of list of hosts to contact is sufficiently large.
|
|