When a server won’t start because a port is already in use, the useful question is not just “which port?” but “which process owns it?” On Windows, netstat -ano gives you the port, connection state, and process ID in one view.
Find the port and its PID
Open Command Prompt or PowerShell and run:
netstat -ano
The options combine three things:
-
-aincludes active connections and listening ports. -
-nkeeps addresses and port numbers numeric rather than resolving names. -
-oadds the owning process ID (PID).
You don’t need an elevated prompt for this output. To narrow the results to entries containing port 3000, for example:
netstat -ano | findstr :3000
Check both address columns: the port might appear as a local port or as part of a remote connection. This is a text search, so it can also match a different port whose number contains 3000; inspect the full line before drawing a conclusion.
A typical result looks like this:
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 4820
TCP 127.0.0.1:53144 127.0.0.1:3000 ESTABLISHED 7624
The first row says a process with PID 4820 is listening on port 3000 on all local IPv4 interfaces. The second row is an active connection to that port; its PID is the process owning that connection, not necessarily the listener.
Turn a PID into a process name
Use tasklist with the PID from the row you’re investigating:
tasklist /FI "PID eq 4820"
This shows the process image name and other basic details. A process can own multiple sockets, so seeing the same PID on several lines is normal. The PID and process name help you investigate; they don’t tell you by themselves whether an application is safe or expected.
For a fuller view, netstat -anob adds executable information. It can be slower and may require an elevated Command Prompt or PowerShell window.
Read the state and address in context
LISTENING means an application has bound to a port and is waiting for incoming TCP connections. ESTABLISHED means a TCP connection is active. TIME_WAIT is part of normal connection closure and usually clears on its own; a PID of 0 on such an entry does not necessarily indicate a problem.
UDP rows look different because UDP does not establish TCP-style connections. They have no connection state, and the foreign address is commonly shown as *:*.
The local address matters too:
-
127.0.0.1is IPv4 loopback. A service bound only here accepts connections addressed to the same machine’s loopback interface. -
0.0.0.0means the service is bound to all local IPv4 interfaces. -
[::]is the IPv6 wildcard address. You may see separate IPv4 and IPv6 listening rows for one service.
A wildcard bind is not the same as public internet access. netstat shows what has bound locally; it does not tell you whether Windows Firewall, a router, NAT, or a cloud firewall allows outside connections. If you’re troubleshooting an SSH server, checking whether SSH is actually running and reachable on Windows means looking beyond the listener entry. For a remote TCP reachability test, Test-NetConnection in PowerShell can help distinguish a local listener from a connection that succeeds across the network.
A PowerShell option for TCP
For scripts or structured filtering, Get-NetTCPConnection returns objects rather than formatted text. For example, to inspect TCP listeners on port 3000:
Get-NetTCPConnection -LocalPort 3000 -State Listen |
Select-Object LocalAddress, LocalPort, State, OwningProcess
To resolve an OwningProcess value, pass its PID to Get-Process, for example:
Get-Process -Id 4820
Get-NetTCPConnection covers TCP; use Get-NetUDPEndpoint when you need UDP endpoints. For a quick interactive check across TCP and UDP, netstat -ano is often the more direct starting point.
A quick troubleshooting sequence
- Run
netstat -ano | findstr :PORTand inspect the matching lines. - Check whether the row is a listener or an existing connection, and note its local address and PID.
- Run
tasklist /FI "PID eq PID_NUMBER"to identify the process. - If the port is listening but clients still cannot connect, check firewall and network rules separately.
This keeps the diagnosis focused: first find the socket, then identify its owner, then test reachability at the layer where the connection is failing.
I originally published a more detailed version of this guide on the SSHFlow blog.
I'm also building SSHFlow — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.
Top comments (0)