netstat, ss and lsof: ports and connections
ipconfig tells you your address; these tools tell you what is talking on it. The question is nearly always one of three: what is listening, what is connected where, and which program has grabbed port 8080.
The three questions, on each platform
| Question | Windows | Linux | macOS |
|---|---|---|---|
| What is listening? | netstat -ano | findstr LISTENINGGet-NetTCPConnection -State Listen | ss -tulpn | lsof -iTCP -sTCP:LISTEN -P -nnetstat -an | grep LISTEN |
| What is connected, to where? | netstat -anoGet-NetTCPConnection -State Established | ss -tanp state established | lsof -i -P -n | grep ESTABLISHEDnetstat -an | grep ESTABLISHED |
| What is using port 8080? | netstat -ano | findstr :8080 then tasklist /fi "PID eq 1234" | ss -tulpn | grep :8080sudo lsof -i :8080 | lsof -i :8080 -P -n |
Process names need elevated rights on every platform: run as administrator (netstat -b), or with sudo for ss -p and lsof to see other users' processes.
Windows: netstat
netstat -ano # all connections and listeners, numeric, with PID
netstat -anob # ...plus the executable name (admin)
netstat -ano | findstr LISTENING
netstat -ano | findstr :443
netstat -ano | findstr ESTABLISHED
netstat -ano -p tcp # TCP only; also udp, tcpv6, udpv6
netstat -e # interface byte and packet counters
netstat -e 5 # refresh every 5 seconds
netstat -s # per-protocol statistics (retransmits, errors)
netstat -r # routing table, same as route print
netstat -an 2 # refresh every 2 seconds Proto Local Address Foreign Address State PID
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1092
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:3389 0.0.0.0:0 LISTENING 1240
TCP 127.0.0.1:8080 0.0.0.0:0 LISTENING 15208
TCP 192.168.1.42:52114 142.250.187.196:443 ESTABLISHED 7724
TCP 192.168.1.42:52130 104.16.132.229:443 TIME_WAIT 0
TCP [::]:135 [::]:0 LISTENING 1092
UDP 0.0.0.0:5353 *:* 2456- Local Address 0.0.0.0:445
- Listening on port 445 on every IPv4 address, including any the machine may get later.
[::]:445is the same for IPv6.127.0.0.1:8080listens only for connections from the machine itself, which is why a development server bound there is not reachable from your phone. - PID 4
- The System process: kernel-mode listeners such as SMB (445), HTTP.sys (80/443 when IIS or some apps use it) and RDP support.
- PID 0 with TIME_WAIT
- A closed connection lingering for a couple of minutes so late packets are discarded safely. Hundreds of these after a busy client session are normal.
Map a PID to a program: tasklist /fi "PID eq 15208", or Task Manager › Details › sort by PID, or Get-Process -Id 15208 | select Name, Path.
PowerShell
Get-NetTCPConnection -State Listen | Sort-Object LocalPort | Format-Table LocalAddress, LocalPort, OwningProcess
Get-NetTCPConnection -LocalPort 8080
Get-NetTCPConnection -State Established | Select-Object RemoteAddress, RemotePort, @{n='Process';e={(Get-Process -Id $_.OwningProcess).ProcessName}}
Get-NetUDPEndpoint | Sort-Object LocalPort
# one-liner: listening ports with process names
Get-NetTCPConnection -State Listen | ForEach-Object { [pscustomobject]@{ Port=$_.LocalPort; Address=$_.LocalAddress; Process=(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName } } | Sort-Object Port | Format-TableReserved port ranges on Windows
If a program fails to bind a port that netstat says is free, Hyper-V or WSL may have reserved a range. netsh interface ipv4 show excludedportrange protocol=tcp lists them. Move the app to another port, or reserve the port explicitly before Hyper-V starts with netsh int ipv4 add excludedportrange protocol=tcp startport=8080 numberofports=1 after temporarily stopping the Hyper-V network service.
Linux: ss
ss ships with iproute2 and replaces netstat, which is in the deprecated net-tools package. The letters are the same.
ss -tulpn # TCP and UDP listeners, numeric, with process (sudo for all PIDs)
ss -tanp # all TCP with process
ss -tan state established
ss -tan state time-wait | wc -l
ss -tlnp 'sport = :22' # filter by local port
ss -tanp dst 192.168.1.10 # connections to one host
ss -tanp dport = :443
ss -s # summary: totals by state
ss -i # TCP internals: rtt, cwnd, retransmits, congestion algorithm
ss -o # timers (keepalive, retransmit)
ss -x # Unix domain sockets
ss -4tln / ss -6tln # by address family
watch -n1 'ss -s' # live counts$ sudo ss -tulpn
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=612,fd=13))
udp UNCONN 0 0 0.0.0.0:5353 0.0.0.0:* users:(("avahi-daemon",pid=701,fd=12))
tcp LISTEN 0 4096 127.0.0.1:631 0.0.0.0:* users:(("cupsd",pid=889,fd=7))
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=891,fd=3))
tcp LISTEN 0 511 *:80 *:* users:(("nginx",pid=1203,fd=6),("nginx",pid=1202,fd=6))
tcp LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=891,fd=4))- Recv-Q / Send-Q
- For listeners: current backlog and its maximum. For connections: bytes not yet read by the application / not yet acknowledged by the peer. A growing Send-Q with a stuck connection means the far end is not reading or the path is black-holed.
- 127.0.0.53%lo:53
- systemd-resolved's stub listener, scoped to the loopback interface. Normal on Ubuntu and Fedora.
- *:80
- A dual-stack listener on all addresses, both families.
Filter syntax is rich: ss -tan '( dport = :443 or sport = :443 )', ss state connected, ss -tan 'sport >= :1024'. Old-style netstat -tulpn and netstat -anp still work if net-tools is installed.
lsof and fuser on Linux
sudo lsof -i :8080 # who has port 8080 (listening or connected)
sudo lsof -i -P -n | grep LISTEN
sudo lsof -i tcp:22
sudo lsof -i @192.168.1.10 # connections to a host
sudo fuser 8080/tcp # just the PID
sudo fuser -k 8080/tcp # kill itmacOS: lsof and netstat
macOS has no ss, and its netstat cannot show process IDs. lsof is the tool.
lsof -iTCP -sTCP:LISTEN -P -n # listening TCP ports with process
sudo lsof -iTCP -sTCP:LISTEN -P -n # including other users' and system processes
lsof -i :8080 -P -n
lsof -i -P -n | grep ESTABLISHED
lsof -iUDP -P -n
netstat -an | grep LISTEN # without process info
netstat -anv | grep LISTEN # -v adds the PID column on recent macOS
netstat -p tcp -s # TCP statistics
netstat -ib # per-interface byte counters
nettop -m tcp # live per-process bandwidth (q to quit)$ lsof -iTCP -sTCP:LISTEN -P -n
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rapportd 612 steve 9u IPv4 0x3a2b 0t0 TCP *:49152 (LISTEN)
ControlCe 640 steve 24u IPv4 0x3a2c 0t0 TCP *:7000 (LISTEN)
node 15208 steve 21u IPv6 0x3a2d 0t0 TCP *:8080 (LISTEN)ControlCenter on ports 5000 and 7000 is AirPlay Receiver, which is what collides with Flask and other dev servers on port 5000; turn it off in System Settings › General › AirDrop & Handoff. rapportd is Continuity. Both are normal.
Connection states
| State | Meaning | When to worry |
|---|---|---|
| LISTEN | A server socket waiting for connections | Only if it is a service you did not expect |
| ESTABLISHED | Open connection carrying data | Never, unless the remote address is unexpected |
| SYN_SENT | You sent a connection request, no reply yet | Many of these mean the target is unreachable or filtering; a few are normal |
| SYN_RECV / SYN_RECEIVED | A client sent SYN, you replied, waiting for the final ACK | Thousands suggest a SYN flood |
| TIME_WAIT | You closed the connection; waiting 60 s (Linux) to 240 s (Windows) for stragglers | Thousands on a busy server are normal; port exhaustion is possible at tens of thousands |
| CLOSE_WAIT | The peer closed; your application has not yet closed its side | Many that never clear indicate an application leaking sockets |
| FIN_WAIT_1 / FIN_WAIT_2 | You closed; waiting for the peer to finish | Stuck ones mean the peer vanished |
| LAST_ACK | Both sides closing; waiting for the final ACK | Rarely |
| CLOSED | Not in use | |
| UNCONN | A UDP socket (ss); UDP has no connections |
Freeing a port that is "already in use"
netstat -ano | findstr :8080
tasklist /fi "PID eq 15208"
taskkill /PID 15208 /FGet-NetTCPConnection -LocalPort 8080 | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force }If the PID is 4 (System), the port is owned by HTTP.sys or a kernel service: netsh http show servicestate lists URL reservations, and netsh http delete urlacl or stopping the World Wide Web Publishing Service releases them. If nothing is listening but binding fails, check the excluded port ranges.
sudo ss -tlnp | grep :8080
sudo fuser -k 8080/tcp
# or
sudo kill $(sudo lsof -t -i :8080)
# a port that is free but still refuses to bind for a minute after a crash is in TIME_WAIT;
# servers should set SO_REUSEADDR, or wait, or:
sudo ss -K dport = :8080lsof -i :8080 -P -n
kill -9 $(lsof -t -i :8080)Bandwidth by process
nettop -m tcp # macOS, built in
sudo iftop -i eth0 # Linux, by connection
sudo nethogs eth0 # Linux, by process
Get-Counter '\Process(*)\IO Read Bytes/sec' # Windows, crude; Resource Monitor › Network is easier (resmon)