I am writing a little TCP/IP server to listen for heartbeats from remote machines and raise an alert if one is missed. To keep things simple rather than spawning child processes to handle connections I start a thread that comunicates through a Thread::Queue. Is this safe to do with IO:Socket ? My code follows with a small script to send the heartbeat at the end. Comments on the code also welcome
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket;
use threads;
use Thread::Queue;
#get the port to bind to or default to 8000
my $port = $ARGV[0] || 8000;
# a hash to record client machines and thread queue for internal comun
+ication
my %clients;
my $queue = Thread::Queue -> new;
my $monitor = threads->create ("monitor", $queue);
#ignore child processes to prevent zombies
$SIG{CHLD} = 'IGNORE';
#create the listen socket
my $listen_socket = IO::Socket::INET->new(LocalPort => $port,
Listen => 10,
Proto => 'tcp',
Reuse => 1);
#make sure we are bound to the port
die "Cant't create a listening socket: $@" unless $listen_socket;
warn "Server ready. Waiting for connections on $port ... \n";
#wait for connections at the accept call
while (my $connection = $listen_socket->accept) {
# spawn a thread to handle the connection
my $child = threads->create ("read_data", $queue, $connection);
}
sub read_data {
# accept data from the socket and put it on the queue
my ($queue, $socket) = @_;
while (<$socket>) {
print "listener got: $_";
$queue -> enqueue(time." $_");
}
}
sub monitor {
my $queue = shift;
while (1) {
while ($queue -> pending) {
my $data = $queue -> dequeue;
print "monitor got: $data";
$data =~ /(\d+) Heartbeat from (\S+) next one in (\d+) min
+utes/;
my $time = $1;
my $client = $2;
my $cycle = $3;
if (defined $clients{$client} and $clients{$client} -> [0]
+ eq 'NAK') {
print "$client sent a beat again\n";
}
$clients{$client} = [ 'OK', $time + $cycle * 60 ];
}
for my $client (keys %clients) {
next if $clients{$client}->[0] eq 'NAK';
next if $clients{$client}->[1] > time;
print "$client missed a heartbeat, expected at $clients{$c
+lient}->[1], now it is ".time."\n";
$clients{$client}->[0] = 'NAK';
}
sleep 30;
}
}
And here is a short script to send heartbeats
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket;
chomp(my $hostname = `hostname`);
$hostname =~ s/\..*//;
my $me = $ARGV[0] || $hostname;
my $cycle_time = $ARGV[1] || 5;
my $port = $ARGV[2] || 8000;
my $ecg = 'rtmr';
while (1) {
print "sending heartbeat\n";
my $socket = IO::Socket::INET->new(PeerAddr => $ecg,
PeerPort => $port,
Proto => "tcp",
Type => SOCK_STREAM)
or die "Couldn't connect to $ecg:$port : $@\n";
print "Heartbeat from $me next one in $cycle_time minutes\n";
print $socket "Heartbeat from $me next one in $cycle_time minutes\
+n";
close($socket);
print "zzzzz......\n";
sleep $cycle_time * 60;
}
Thanks in advance,
R.
Pereant, qui ante nos nostra dixerunt!