Skip to content
Networking
Lab 4 of 7·30mBeginner

Ports, sockets, and telling failures apart

Run your own server, connect to it, and learn to distinguish refused from filtered from "connected but silent" — the three answers that point at three different teams.

You need

  • A Linux system with sudo access
  • netcat and tcpdump (apt-get install -y netcat-openbsd tcpdump)

Do first

"It times out" and "connection refused" look equally broken and mean opposite things. One says the service is down; the other says the packet never arrived. Knowing which is most of the diagnosis.

1. Run a server and connect to it

Terminal one:

nc -l -p 9000

Terminal two:

sudo ss -tlnp 'sport = :9000'
nc 127.0.0.1 9000

Type in either terminal; it appears in the other. That is a TCP socket: a pair of address-and-port endpoints with a byte stream between them. Ctrl-C to close, and notice the other end sees the close immediately.

Verify

sudo ss -tln 'sport = :9000' | tail -n +2 | wc -l # 1 while the listener is running

2. Bind address decides who can reach it

nc -l 127.0.0.1 9001 &
sleep 1
sudo ss -tlnp 'sport = :9001'
nc -z -w2 127.0.0.1 9001; echo "via loopback: exit=$?"
MYIP=$(ip route get 1.1.1.1 | grep -oP 'src \K\S+')
nc -z -w2 "$MYIP" 9001; echo "via real address: exit=$?"
kill %1 2>/dev/null

Bound to 127.0.0.1, it answers on loopback and refuses on the machine's real address. Now the opposite:

nc -l 0.0.0.0 9002 &
sleep 1
nc -z -w2 "$MYIP" 9002; echo "via real address: exit=$?"
kill %1 2>/dev/null

0.0.0.0 means every address. This is the single most common cause of "works locally, times out from anywhere else" — and it is a one-word config change, not a firewall problem.

Verify

nc -l 0.0.0.0 9002 & sleep 1 nc -z -w2 "$(ip route get 1.1.1.1 | grep -oP 'src \K\S+')" 9002 echo "exit=$?"; kill %1 2>/dev/null # exit=0

3. The three failure modes

Refused — the packet arrived, nothing was listening:

time nc -z -w5 127.0.0.1 9099; echo "exit=$?"

Immediate. Routing and firewall are fine; the service is down or on a different port.

Filtered — something dropped the packet silently:

time nc -z -w5 10.255.255.1 9099; echo "exit=$?"

Hangs for the full timeout. That is a firewall, a security group, or a missing route — not the service.

Connected but silent — the handshake completes and nothing replies. That is a live socket with a wedged application behind it, and it is the one that fools health checks that only test whether the port opens.

SymptomMeaningWho fixes it
Refused, instantlyNothing listening on that portService owner
TimeoutPacket dropped in transitNetwork / firewall
Connects, no replyApplication accepted and stalledService owner

Verify

nc -z -w2 127.0.0.1 9099 2>&1; echo "refused exit=$?" # refused exit=1, returned immediately rather than after 2s

4. Watch the handshake

nc -l 0.0.0.0 9003 &
sudo timeout 8 tcpdump -n -i lo "tcp port 9003" &
sleep 1
nc -z -w2 127.0.0.1 9003
wait
kill %1 2>/dev/null

Read the flags. [S] is the client's SYN, [S.] is the server's SYN-ACK, [.] is the client's ACK — the three-way handshake. Then [F.] or [R] closing it.

What you see tells you where it stopped:

  • SYN with no reply → dropped outbound, or the reply is dropped
  • SYN then [R] (reset) → actively refused
  • Full handshake then silence → the network is fine, the application is not answering

Verify

sudo timeout 6 tcpdump -n -c 2 -i lo "tcp port 22 or tcp port 9003" 2>/dev/null | wc -l # 0 or more; the command completes rather than erroring

5. Which process owns a port

sudo ss -tlnp | head -n 8
sudo lsof -i :22 2>/dev/null | head -n 3
sudo fuser -n tcp 22 2>/dev/null

Three routes to the same answer, and you will meet all three because different boxes have different tools installed. This is what you run against "address already in use" — something is holding the port, and these name it.

Verify

sudo ss -tlnp 'sport = :22' | grep -c users # 1 — the owning process is named

6. Well-known ports worth knowing by heart

PortService
22SSH
53DNS (UDP and TCP)
80 / 443HTTP / HTTPS
3306MySQL
5432PostgreSQL
6379Redis
8080HTTP alternate

Ports below 1024 require root to bind, which is why a web server starts as root and drops privileges, and why a container running as non-root cannot listen on 80 without a capability or a port mapping.

Verify

sudo ss -tln | grep -cE ':(22|53|80|443)\s' || echo "0" # 1 or more of the well-known ports in use

Clean up

pkill -f "nc -l" 2>/dev/null; echo "listeners stopped"

Where this goes next

You can reach a port and classify a failure. Next: building a network between two hosts on one machine, so routing stops being abstract.