A TCP Ping perl script for Microsoft Windows

Blue Bar separator

The following Perl script can be used to "ping" a host. It differs from the typical ping because it makes a TCP connection instead of using the ICMP protocol. This allows it to pass through firewalls and other filters that block ICMP. As long as the host has an open TCP port this script can be used.

You can also use it on closed ports assuming that the host responds to the connection request with a "reset" acknowledgment packet. Some hosts do not respond and of course firewalls and other filters may block connection requests to closed ports so the host never gets the request. In addition windows tries 3 times to make a connection even if it receives a reset acknowledgement so the times will not reflect the actual response time but it will indicate that the host can be reached. In general it is best to use open ports.

The script sets a linger time of 0 so that when the connection is closed it sends a reset packet instead of FIN packet. This bypasses the socket's TIME_WAIT state. The advantage of this is that the script never uses more than the 1 socket.

The script takes 3 arguments. First, the name or IP address of the host being tcp-pinged, second the port number and finally the delay between tcp-pings.

The output is in 3 parts. The first is just a line repeating the input arguments with the target IP address in parens. The second part is a header for the columns and the current date and time. The third part is the actual results of the tping in 3 columns. The first column is the number of successful tpings, that is the number of connection requests that completed or that received a reset, followed by the total number of tping tries and the percentage of completed requests. The second column is the minimum, average and maximum response times. This is only calculated for successes. An unsucessful try does change either the maximum or the average values. The third column is an explaination of the results for that try, It may indicate that the connection completed or was refused or an indictaion of the type of failure. It also includes the actual response time for that try.

Every 22 tries the header line is repeated refreshing the date and time. On its own the script will run forever, to terminate it type control-C.

This Perl script was written using ActivePerl 5.8 for Microsoft Windows. I expect that it will run without problems under other Perls on Windows. However, when I ran this script on a Linux system it did not work as expected - the error codes returned by the connect statement where different. Hence this script should only be run under Windows, which is why the name is tping-w.pl instead of just tping.pl.



Example 1 - If you ping www.microsoft.com using the traditional ping command you will not get a response but you will get a response using tping-w.pl. Note that the ping command uses the name "www.microsoft.com" while the output reports the name "lb1.www.ms.akadns.net". The name www.microsoft.com resolves to many IP addresses, the official name of the host whose address was used was lb1.www.ms.akadns.net. The ping command does another lookup of the address to get its official name, the tping-w.pl script doesn't do this lookup and uses the name on the command line.

C:\>ping www.microsoft.com                                                      

Pinging lb1.www.ms.akadns.net [207.46.20.60] with 32 bytes of data:

Request timed out.
Request timed out.
Request timed out.
Request timed out.

Ping statistics for 207.46.20.60:
    Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),

    
C:\>perl tping-w.pl www.microsoft.com 80 1
perl tping-w.pl www.microsoft.com (207.46.20.60) 80 1


Success/Tries=% min/average/max success time       Mon Oct 30 13:59:56 2006
1/1=100.000%    152.872/152.872/152.872   Reply (connected) in 152.872 ms
2/2=100.000%    66.108/109.490/152.872   Reply (connected) in 66.108 ms
3/3=100.000%    43.391/87.457/152.872   Reply (connected) in 43.391 ms
4/4=100.000%    41.292/75.916/152.872   Reply (connected) in 41.292 ms
Terminating on signal SIGINT(2)



Example 2 shows the results when you tping-w a host that does not respond.

C:\>perl tping-w.pl 192.168.1.1 88 1                                            
perl tping-w.pl 192.168.1.1 (192.168.1.1) 88 1


Success/Tries=% min/average/max success time       Mon Oct 30 14:01:23 2006
0/1=0.000%      999999.000/0.000/0.000   No reply (timeout) after 21034.785 ms
0/2=0.000%      999999.000/0.000/0.000   No reply (timeout) after 21031.25 ms
0/3=0.000%      999999.000/0.000/0.000   No reply (timeout) after 41154.625 ms
0/4=0.000%      999999.000/0.000/0.000   No reply (timeout) after 20937.5 ms
Terminating on signal SIGINT(2)



Example 3 shows the difference when you tping-w an open port and a closed port that does respond with a reset ack packet. Note that the average response time goes from 65 ms to over a second. Again this is a result of how Microsoft Windows handles the reset ack response - making multiple attempts before returning an error back to the calling program.

C:\>perl tping-w.pl 10.15.7.34 23 1
perl tping-w.pl 10.15.7.34 (10.15.7.34) 23 1


Success/Tries=% min/average/max success time       Mon Oct 30 14:12:53 2006
1/1=100.000%    69.986/69.986/69.986   Reply (connected) in 69.986 ms
2/2=100.000%    69.986/76.307/82.627   Reply (connected) in 82.627 ms
3/3=100.000%    46.611/66.408/82.627   Reply (connected) in 46.611 ms
4/4=100.000%    46.611/65.806/82.627   Reply (connected) in 63.999 ms
Terminating on signal SIGINT(2)

C:\>perl tping-w.pl 10.15.7.34 25 1                                             
perl tping-w.pl 10.15.7.34 (10.15.7.34) 25 1


Success/Tries=% min/average/max success time       Mon Oct 30 14:12:32 2006
1/1=100.000%    1080.826/1080.826/1080.826   Reply (refused) in 1080.826 ms
2/2=100.000%    1080.826/1087.288/1093.750   Reply (refused) in 1093.75 ms
3/3=100.000%    1080.826/1094.650/1109.375   Reply (refused) in 1109.375 ms
4/4=100.000%    1080.826/2481.144/6640.625   Reply (refused) in 6640.625 ms
Terminating on signal SIGINT(2)



Here is the script.

#! ./perl
#
# tping-w.pl
# Version 1.4 06-10-30
# Version 1.5 10-11-26 added disclaimer
# Noah Davids ndav1@cox.net
#
# This software is provided on an "AS IS" basis, WITHOUT ANY WARRANTY OR ANY
# SUPPORT OF ANY KIND. The AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES
# OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE.  This disclaimer
# applies, despite any verbal representations of any kind provided by the
# author or anyone else.
#
use strict;
use strict;
use warnings;

use Socket;
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
use Time::localtime;

our ($target, $port, $delay);
our ($iaddr, $proto, $sin);
our ($seconds1, $microseconds1, $seconds2, $microseconds2, $us1, $us2, $ms);
our ($e, $success, $tries, $min, $ave, $max, $message);
our ($linger);
our ($now);

$target = $ARGV[0] || die "Usage $0 target_IP_addr port_number delay_in_seconds";
$port = $ARGV [1]  || die "Usage $0 target_IP_addr port_number delay_in_seconds";
$delay = $ARGV [2] || die "Usage $0 target_IP_addr port_number delay_in_seconds";

(undef, undef, undef, undef, $iaddr) = gethostbyname($target);
if (not (defined $iaddr))
   {
	   $iaddr = inet_aton($target);
	   if (not (defined $iaddr))
	   {
		   die "$target cannot be resolved into an IP address";
	   }
   }
   
print "perl ". $0 . " " . $target . " (" . inet_ntoa ($iaddr) . ") " .$port . " " . $delay . "\n\n";

$min = 999999;
$max = 0;
$ave = 0;
$success = 0;
$tries = 0;
$linger = pack ("sll", 1, 0, 0);

while (1) {
	if (($tries % 22) == 0) {
		$now = ctime ();
		print "\nSuccess/Tries=%\tmin/average/max success time\t   $now\n";
	}
	$tries++;
	$proto = getprotobyname('tcp');
    socket(Socket_Handle, PF_INET, SOCK_STREAM, $proto);
    $sin = sockaddr_in($port,$iaddr);
    setsockopt (Socket_Handle, SOL_SOCKET, SO_LINGER, $linger) || die "setsockopt: $!";
    ($seconds1, $microseconds1) = gettimeofday;
    if (connect(Socket_Handle,$sin)) {
	    $e = 0;
    }
    else {
	    $e = $!;
    }
    close(Socket_Handle);
    ($seconds2, $microseconds2) = gettimeofday;
    $us1 = $seconds1 * 1000000 + $microseconds1;
    $us2 = $seconds2 * 1000000 + $microseconds2;
    $ms = ($us2 - $us1) / 1000;

    if ($e == 0)
    {
	    $message = "Reply (connected) in " . $ms . " ms\n";
	    if ($ms < $min) {
		    $min = $ms;
	    }
	    if ($ms > $max) {
		    $max = $ms;
	    }
	    $ave = ($ave * $success);
	    $success++;
	    $ave = ($ave + $ms) / $success;
    }
    elsif ($e == 10061) { # connection refused
	    $message = "Reply (refused) in " . $ms . " ms\n";
	    if ($ms < $min) {
		    $min = $ms;
	    }
	    if ($ms > $max) {
		    $max = $ms;
	    }
	    $ave = ($ave * $success);
	    $success++;
	    $ave = ($ave + $ms) / $success;
    }
    elsif ($e == 10060) { # timeout
    	$message = "No reply (timeout) after " . $ms . " ms\n";
    	}
    elsif ($e == 10051) { # network unreachable
    	$message = "No reply (net unreach) after " . $ms . " ms\n";
    	}
    elsif ($e == 10065) { # host unreachable
    	$message = "No reply (host unreach) after " . $ms . " ms\n";
    	}
    else {
	    printf ("Unknown error %d returned from connect iteration ignored\n", $e);
	    $message = "";
	    $tries--;
    }
    if ($tries > 0) {
	    printf ("%d/%d=%3.3f%%\t%.3f/%.3f/%.3f   %s", $success, $tries, $success/$tries*100, $min, $ave, $max, $message);
    }
    else {
	    printf ("%d/%d=  0.000%%\t%.3f/%.3f/%.3f   %s", $success, $tries, $min, $ave, $max, $message);
    }
    sleep $delay;
}
    
# tping-w.pl ends here



Blue Bar separator
This page was last modified on 10-11-26
mailbox Send comments and suggestions
to ndav1@cox.net