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.