UDP sockets don't block, which makes them easy to work with in eventy
systems. This recipe builds upon the vanilla UDP recipe by
subscribing each peer to a group. Multiple peers may exchange data
with very little work. All the UDP rules still apply. See
/UDP Sockets for additional details.
#!/usr/bin/perl
use warnings;
use strict;
use POE;
use IO::Socket::Multicast;
use constant DATAGRAM_MAXLEN => 1024;
use constant MCAST_PORT => 32003;
use constant MCAST_GROUP => '230.1.2.3';
use constant MCAST_DESTINATION => MCAST_GROUP . ':' . MCAST_PORT;
POE::Session->create(
inline_states => {
_start => \&peer_start,
get_datagram => \&peer_read,
send_something => \&send_something,
}
);
POE::Kernel->run();
exit;
### Set up the peer socket.
sub peer_start {
my $kernel = $_[KERNEL];
# Don't specify an address.
my $socket = IO::Socket::Multicast->new(
LocalPort => MCAST_PORT,
ReuseAddr => 1,
ReusePort => 1,
) or die $!;
$socket->mcast_add(MCAST_GROUP) or die $!;
# Don't mcast_loopback(0). This disables multicast datagram
# delivery to all peers on the interface. Nobody gets data.
# Begin watching for multicast datagrams.
$kernel->select_read($socket, "get_datagram");
# Send something once a second. Pass the socket as a continuation.
$kernel->delay(send_something => 1, $socket);
}
### Receive a datagram when our sicket sees it.
sub peer_read {
my ($kernel, $socket) = @_[KERNEL, ARG0];
my $remote_address = recv($socket, my $message = "", DATAGRAM_MAXLEN, 0);
die $! unless defined $remote_address;
chomp $message;
my ($peer_port, $peer_addr) = unpack_sockaddr_in($remote_address);
my $human_addr = inet_ntoa($peer_addr);
print "received from $human_addr : $peer_port ... $message\n";
}
### Periodically send something.
sub send_something {
my ($kernel, $socket) = @_[KERNEL, ARG0];
my $message = "pid $$ sending at " . time() . " to " . MCAST_DESTINATION;
warn $! unless $socket->mcast_send($message, MCAST_DESTINATION);
$kernel->delay(send_something => 1, $socket);
}