This program pings an arbitrarily large list of addresses at once. It is bounded by practical limitations like memory and system overhead. It requires POE::Component::Client::Ping (and POE, of course).

The hosts to ping are listed as numeric addresses to avoid DNS delays. If you'd rather use host names, consider using non-blocking /DNS lookups instead of gethostbyname() or inet_aton().

#!/usr/bin/perl -w
# $Id$
use warnings;    # or -w above
use strict;

  die "POE::Component::Client::Ping requires root privilege\n"
    if $> and ($^O ne 'VMS');
use POE;
use POE::Component::Client::Ping;

# How many seconds to wait for ping responses.
sub PING_TIMEOUT () { 5 }

# A bunch of addresses to ping, culled from random NMIDA-infected web
# servers that have visited my dialup machine at one time or another.
my @addresses = qw(

# The main loop.
# Create a pinger component.  This will do the work of multiple
# concurrent pings.  It requires another session to interact with it.
  Alias   => 'pinger',        # The component's name will be "pinger".
  Timeout => PING_TIMEOUT,    # The default ping timeout.

# Create a session that will use the pinger.  Its parameters match
# event names with the functions that will handle them.
  inline_states => {
    _start => \&client_start,       # Call client_start() to handle "_start".
    pong   => \&client_got_pong,    # Call client_got_pong() to handle "pong".

# Start POE's main loop.  It will only return when everything is done.

# Event handlers.
# Handle _start (given by POE itself to start your session) by sending
# several "ping" commands to the component at once.  The component
# will reply over the course of PING_TIMEOUT seconds.
sub client_start {
  my ($kernel, $session) = @_[KERNEL, SESSION];
  print "Starting to ping hosts.\n";
  foreach my $address (@addresses) {
    print "Pinging $address at ", scalar(localtime), "\n";

    # "Pinger, do a ping and return the results as a pong event.  The
    # address to ping is $ping."
    $kernel->post(pinger => ping => pong => $address);

# Handle a "pong" event (returned by the Ping component because we
# asked it to).  Just display some information about the ping.
sub client_got_pong {
  my ($kernel, $session) = @_[KERNEL, SESSION];

  # The original request is returned as the first parameter.  It
  # contains the address we wanted to ping, the total time to wait for
  # a response, and the time the request was made.
  my $request_packet = $_[ARG0];
  my ($request_address, $request_timeout, $request_time) = @{$request_packet};

  # The response information is returned as the second parameter.  It
  # contains the response address (which may be different from the
  # request address), the ping's round-trip time, and the time the
  # reply was received.
  my $response_packet = $_[ARG1];
  my ($response_address, $roundtrip_time, $reply_time) = @{$response_packet};

  # It is impossible to know ahead of time how many ICMP ping
  # responses will arrive for a particular address, so the component
  # always waits PING_TIMEOUT seconds.  An undefined response address
  # signals that this waiting period has ended.
  if (defined $response_address) {
    printf("Pinged %-15.15s - Response from %-15.15s in %6.3fs\n",
      $request_address, $response_address, $roundtrip_time);
  else {
    print "Time's up for responses from $request_address.\n";