What Is Using a Port
The most common reason either command gets run. Find the PID that owns the port, then name it.
| Platform | Command | What it does |
|---|---|---|
| Windows | PID of whatever holds port 8080, last column | |
| Windows | Name the program behind PID 4312 | |
| Windows | Executable name inline (elevated prompt, slower) | |
| Windows | Free the port by killing its owner | |
| Linux | Listener on 8080 with process name and PID | |
| Linux | Same answer without the filter syntax | |
| Linux | Just the PIDs; add to kill them | |
| macOS | Which process is on port 8080 | |
| macOS | Listener only, no name lookups |
PowerShell does it in one step, no findstr and no second command:
Get-Process -Id (Get-NetTCPConnection -LocalPort 8080 -State Listen).OwningProcessFor open files, sockets, and deleted-file handles beyond ports, see the lsof cheat sheet.
Windows netstat
netstat is not deprecated on Windows. Run these in Command Prompt or PowerShell; is the Windows grep, and needs an elevated prompt.
| Command | What it does |
|---|---|
| All connections and listeners, numeric, with PID | |
| Filter to one port | |
| Listeners only | |
| Live connections only | |
| Show the executable name inline (needs an admin prompt, slower) | |
| TCP only (, also work) | |
| Name the process behind PID 4312 | |
| Routing table | |
| Interface byte and packet counters | |
| Interface counters plus per-protocol statistics | |
| Reprint every 5 seconds (Ctrl+C stops) | |
| Kill the process holding a port |
netstat to ss Mapping
netstat is deprecated on Linux; ss is the replacement and takes the same flag letters for the common cases. The last two rows moved to instead.
| netstat | ss / ip | What it shows |
|---|---|---|
| Listening TCP ports with the owning process | ||
| Listening UDP ports with the owning process | ||
| Both, one table | ||
| All sockets, listening and connected | ||
| Current TCP connections, numeric | ||
| Summary statistics by protocol | ||
| Routing table | ||
| Interfaces with packet and error counters |
The letters mean: TCP, UDP, listening only, numeric (skip DNS, always worth it), process info (needs for other users' processes), all.
Listening Ports
| Command | What it does |
|---|---|
| Every listening TCP port and who owns it | |
| Is something on port 80 | |
| Same, filtered by ss itself | |
| TCP and UDP listeners together | |
| Listening sockets without process info (no sudo needed) | |
| Which port is sshd actually on |
If a port shows a listener bound to , it only accepts local connections; or means all interfaces. To test a port from the outside instead, use the SSH port checker.
Established Connections
| Command | What it does |
|---|---|
| All current TCP connections | |
| Only live connections | |
| Live connections with the owning process | |
| Outbound HTTPS connections | |
| Connections to one host | |
| How many sockets in TIME_WAIT |
Recipes:
# Who is connected to my web server right now, by IP:
ss -tn state established '( sport = :443 or sport = :80 )'
# Count connections by state (spot SYN floods and CLOSE_WAIT leaks):
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
# Count established connections per client IP:
ss -tn state established | awk 'NR>1 {split($5,a,":"); print a[1]}' | sort | uniq -c | sort -rnConnection States
The State column is the TCP state machine. UDP rows have no state, which is why they look empty.
| State | What it means | When it matters |
|---|---|---|
| Server socket waiting for clients | Missing means nothing is bound to that port | |
| Handshake done, data can flow | One row per live connection | |
| You asked to connect, nothing answered | Firewall dropping packets, or wrong host | |
| Handshake half-finished on your side | Thousands of these is a SYN flood | |
| You closed it, kernel holds it ~60s | Normal in bulk; only hurts clients out of ports | |
| Peer closed, your app never did | Piles up only because of a bug in your program | |
| You closed, peer never finished closing | Usually the peer crashed or was firewalled off | |
| Waiting for the final ack before release | Transient; stuck ones mean a dead peer |
| Command | What it does |
|---|---|
| Totals by state and protocol, one screen | |
| Every leaked socket, with the local port | |
| Count TIME_WAIT sockets on Windows | |
| Connection attempts nothing is answering on Windows |
ss Filters
ss filters sockets itself, no grep needed. Quote the expression, and put spaces around the parentheses and operators.
| Command | What it does |
|---|---|
| Filter by state (, , , also work) | |
| By destination port | |
| By source (local) port | |
| Either port | |
| Connections to a subnet | |
| Port comparisons work too | |
| Kill matching connections (kernel support required) |
Routing Table and Stats
The parts of netstat that ss does not cover, and their modern forms.
| Command | What it does |
|---|---|
| Routing table (); the line is your gateway | |
| Which route and interface a destination would use | |
| Interfaces with RX/TX and error counters () | |
| Socket totals by state and protocol ( is more detailed) | |
| Full per-protocol counters: retransmits, resets, ICMP (needs net-tools) |
macOS netstat
macOS ships a BSD netstat with different flags, and no for processes ( selects a protocol instead). Use for the process view.
| Command | What it does |
|---|---|
| Listening ports | |
| TCP sockets ( for UDP) | |
| Which process is on port 8080 | |
| Every listener with its process | |
| Routing table |
netstat Flags
Two combinations cover most of it: on Linux, on Windows. The same letter often means something different on each.
| Flag | Linux netstat | Windows netstat |
|---|---|---|
| All sockets, listening and connected | Same | |
| Numeric, skip DNS and service names | Same | |
| / | TCP only / UDP only | Not available; use or |
| Listening sockets only | Not available; pipe to | |
| Show PID and process name | Selects a protocol instead; gives the PID | |
| Show connection timers | Show the owning PID | |
| Not available | Show the owning executable (elevated only) | |
| Routing table | Routing table | |
| Extended output: user and inode | Ethernet statistics | |
| Interface table | Not available; is the closest | |
| Per-protocol statistics | Same | |
| Repeat every second | Use a trailing number: |
Reading the Output
Both tools print the same columns, in the same order, on every platform.
| Column | What it means |
|---|---|
| , , ; a row bound to usually serves IPv4 too | |
| Bytes received that the program has not read; steady non-zero means it is falling behind | |
| Bytes sent but not acknowledged; on a row it is the accept backlog | |
| Your side. or is every interface, is local only | |
| The other end. on a listening row just means nobody is connected | |
| TCP state; blank for UDP, which has none | |
| Owner, needs . Windows shows a bare PID unless you use |
Install netstat
Only worth doing when a script insists on it; is already installed everywhere.
| Command | Distro |
|---|---|
| Debian, Ubuntu (apt cheat sheet) | |
| Fedora, RHEL, Rocky, Alma | |
| RHEL 7 and older | |
| Arch | |
| openSUSE | |
| Alpine, and most slim containers |
If says it cannot locate the package, run first; the package list in a fresh container image is usually empty.
Gotchas
- on a modern distro is expected: the default answer is , and brings netstat back if a script insists on it.
- Without , the column is blank for processes owned by other users, including every system service. No error, just missing names.
- Always add . Without it both tools resolve every address and port name, which is slow and turns into mid-scan.
- ss filter expressions are picky about spacing: works, does not.
- A service bound to will look fine in but be unreachable from outside. Check the Local Address column, not just the port.
- On Windows, PID 4 is the kernel. parks ports there for IIS, WinRM, and WSL, so will not help; stop the service that registered the URL instead.
- A Windows port that refuses binds while shows nothing is usually inside a reserved range. lists them, and Hyper-V grabs large blocks on every boot.
- Neither tool can look at another machine. Run it over SSH, or scan from outside with nmap.
- Connections you see in ss can still be invisible to clients if the firewall drops packets first. When listeners look right but connections fail, capture with tcpdump and check iptables.