Port Testing with PowerShell

Just a few lines of code suffice to add a powerful and flexible port testing command to PowerShell. Test-PSOnePort can test a single port with a timeout - once, or continously until it responds.

Just a few lines of code suffice to add a powerful and flexible port testing command to PowerShell. Test-PSOnePort can test a single port with a timeout - once, or continously until it responds.

Why Testing Ports?

There are many reasons why testing a port can be useful. Most computers these days do not respond to ping (ICMP) requests, but computers always respond to vital ports. So testing for a given port may help you check the online status of a computer.

Often, it is useful to test a specific port to see if a required functionality is running. For example, I was using a local webserver while working on this website to test web pages locally. To automate things, I needed to know whether the local webserver was running already or needed to be started.

So I was looking for a way to test port 4000, the port that my webserver was listening on. Test-NetConnection can do this, and so I tried:

Test-NetConnection -ComputerName 127.0.0.1 -Port 4000

It worked, but when the webserver was not running and port 4000 therefore unavailable, it took a really long time for the command to return. Sadly, as with many networking tools, there was no way to specify a timeout with Test-NetConnection.

Creating a Better Port Tester

So let’s take our favorite tool PowerShell and quickly implement our own brand new Test-PSOnePort command!

Test-PSOnePort of course can test a port with a timeout so you don’t have to wait when ports are unavailable.

This line tests for port 4000 and waits a maximum of 1000 milliseconds for a response:

Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000

To test ports on your local computer, you may need to run the command from an elevated PowerShell instance.

You can also continuously wait for a port to respond, and continue once the port is up:

Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000 -Count 0 -Delay 2000 -ExitOnSuccess

Coincidentally, that was exactly the solution I was looking for: I am now using this line to wait for the local webserver to spin up, and once it is ready for action, PowerShell immediately launches a couple of local webpages to test-drive.

I am test-driving website with Google Chrome Incognito Mode. It won’t cache anything which is great for testing - because that way, I don’t have to refresh the browser cache when a website is updated. Here is how I launch test websites via PowerShell:

$app = "${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe"
$arguments = "-incognito --new-window http://127.0.0.1:4000"
Start-Process -FilePath $app -ArgumentList $arguments -WindowStyle Maximized

Implementing Test-PSOnePort

Here is the implementation of Test-PSOnePort:

function Test-PSOnePort
{
  <#
      .SYNOPSIS
      Tests a network port on a remote computer

      .DESCRIPTION
      Tests whether a port on a remote computer is responding.

      .EXAMPLE
      Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000 
      Tests whether port 4000 on the local computer is responding, 
      and waits a maximum of 1000 milliseconds

      .EXAMPLE
      Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000 -Count 30 -Delay 2000
      Tests 30 times whether port 4000 on the local computer is responding, 
      and waits a maximum of 1000 milliseconds inbetween each test

      .EXAMPLE
      Test-PSOnePort -ComputerName 127.0.0.1 -Port 4000 -Timeout 1000 -Count 0 -Delay 2000 -ExitOnSuccess
      Continuously tests whether port 4000 on the local computer is responding, 
      waits a maximum of 1000 milliseconds inbetween each test, 
      and exits as soon as the port is responding

      .LINK
      https://powershell.one/tricks/network/porttest
  #>


  param
  (
    [Parameter(Mandatory)]
    [string]
    $ComputerName,
        
    # port number to test
    [Parameter(Mandatory)]
    [int]
    $Port,

    # timeout in milliseconds
    [int]
    $Timeout = 500,

    # number of tries. A value of 0 indicates countinuous testing
    [int]
    [ValidateRange(0,1000)]
    $Count = 1,
        
    # delay (in milliseconds) inbetween continuous tests
    $Delay = 2000,
        
    # when enabled, function returns as soon as port is available
    [Switch]
    $ExitOnSuccess
  )
  $ok = $false
  $c = 0
  $isOnline = $false
  $continuous = $Count -eq 0 -or $Count -gt 1
  try
  {
    do
    {
      $c++
      if ($c -gt $Count -and !$continuous) { 
        # count exceeded
        break
      }
      $start = Get-Date
            
      $tcpobject = [system.Net.Sockets.TcpClient]::new()
      $connect = $tcpobject.BeginConnect($computername,$port,$null,$null) 
      $wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false) 
        
      if(!$wait) { 
        # no response from port
        $tcpobject.Close()
        $tcpobject.Dispose()
                
        Write-Verbose "Port $Port is not responding..."
        if ($continuous) { Write-Host '.' -NoNewline }
      } else { 
        try { 
          # port is reachable
          if ($continuous) { Write-Host '!' -NoNewline }
          [void]$tcpobject.EndConnect($connect)
          $tcpobject.Close()
          $tcpobject.Dispose()
                    
          $isOnline = $true
          if ($ExitOnSuccess)
          {
            $ok = $true
            $delay = 0
          }
        }
        catch { 
          # access to port restricted
          throw "You do not have permission to contact port $Port."
        } 
      } 
      $stop = Get-Date
      $timeUsed = ($stop - $start).TotalMilliseconds
      $currentDelay = $Delay - $timeUsed
      if ($currentDelay -gt 100)
      {
        Start-Sleep -Milliseconds $currentDelay
      }
    
    } until ($ok)
  }
  finally
  {
    # dispose objects to free memory
    if ($tcpobject)
    {
      $tcpobject.Close()
      $tcpobject.Dispose()
    }
  }
  if ($continuous) { Write-Host }
    
  return $isOnline
}

The key is the use of a TcpClient object which can connect to any port asynchronously in the background. So PowerShell can continue to run in the foreground while the TcpClient is trying to connect.

The timeout is handled by the method WaitOne() which returns when the port responds or the specified timeout is elapsed - whichever comes first.

A little care is needed to dispose used objects or else your script will quickly hog a lot of memory. This is done by properly calling the EndConnect() method if the port responded, and then closing and disposing the TcpClient object.

Get Source Code

Don’t bother copy&pasting the source code. Test-PSOnePort is part of the PowerShell module PSOneTools. Simply install it from the PowerShellGallery to immediately start using Test-PSOnePort in your PowerShell:

Install-Module -Name PSOneTools -Scope CurrentUser -Force

If you’d like to take a look at the source code, or feel intreagued to file a bug report or help improve the code, simply visit the PSOneTools GitHub repository.