Skip to content

Introduction to ports and protocols

An IP address gets a packet to the right machine. That's not the same as getting it to the right program running on that machine. A single server might run a web server, a mail server, and an SSH daemon simultaneously, all sharing one IP address — so something else has to say, within that one machine, which of those programs a given piece of data is actually meant for. That something is a port number.

Why ports exist

Think back to the client-server exchange from What is a computer network?: a browser connects to a server, sends an HTTP request, and gets a response. Now imagine that same server is also running a mail service and a database. All three services are reachable at the same IP address. When a packet arrives, the operating system needs to hand it to the correct running process — not guess, not broadcast it to all three, but deliver it to exactly one.

A port is a 16-bit number, from 0 to 65535, that identifies a specific communication endpoint on a host. It lives in the Transport layer header — TCP and UDP both carry a source port and a destination port in every segment or datagram they send. When your browser opens a connection to a web server, it's really connecting to the combination of that server's IP address and port 443 (the standard HTTPS port) — not just the IP address alone.

Explain it in 30 seconds

If an interviewer asks you to define a port on the spot, the answer that actually demonstrates understanding is short: a port is a 16-bit number in the TCP or UDP header that tells the receiving host's kernel which process should get this data. It only means anything paired with an IP address and a transport protocol — a port number alone, with nothing else, doesn't identify a destination at all. That three-part combination is a socket, and it's the real unit of "who is talking to whom" on the internet.

Sockets: IP address, port, and protocol together

The combination of an IP address, a port number, and a transport protocol (TCP or UDP) is called a socket. A socket uniquely identifies one endpoint of one specific network conversation. Because TCP and UDP are counted separately, a host can have a TCP service and a UDP service both using port 53 at the same time without conflict — DNS does exactly this, using UDP port 53 for typical lookups and TCP port 53 for responses too large to fit in a single UDP datagram.

A full TCP connection is actually identified by two sockets together — the source IP:port and the destination IP:port — which is how a server can serve thousands of simultaneous clients on the same destination port (443, say) without confusing one client's traffic for another's: each client connects from a different source port, or a different source IP, or usually both.

Client 203.0.113.5:51342  <--->  Server 198.51.100.10:443
        (source socket)              (destination socket)

This idea — that a connection is defined by both ends' address and port together, not just the destination — comes back later in the course, wherever a device in the middle has to keep track of many connections at once and match each reply to the right one.

The three port ranges

The Internet Assigned Numbers Authority (IANA), which maintains the official registry of port assignments, divides the full 0–65535 range into three bands, each with a different purpose and a different set of rules for how a number gets assigned to a service:

Range Numbers IANA's own name Purpose
Well-known ports 0–1023 System Ports Long-established, standardized services (HTTP, SSH, DNS)
Registered ports 1024–49151 User Ports Assigned to specific applications by request, less strictly enforced
Dynamic/private ports 49152–65535 Dynamic Ports Temporary, assigned automatically for outgoing client connections

Both sets of names are in common use — the registry says "System Ports," everyone in practice says "well-known ports," and they mean the same range. Each band gets a dedicated article, because the rules and reasoning behind each differ enough to matter: Well-known ports, Registered ports, and Dynamic port range.

Reading ports in real command output

ss shows active connections and listening ports on Linux. (You'll still find netstat in older guides; it's deprecated on current distributions in favour of ss, which reads kernel socket state directly and is considerably faster on a busy host.)

The -p flag asks for the process behind each socket, and that's the part that needs privilege — reading which process owns a socket means looking at other users' processes, so without sudo the process column comes back mostly empty:

sudo ss -tulpn
Netid State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp   UNCONN 0      0            0.0.0.0:68        0.0.0.0:*     users:(("dhclient",pid=712,fd=6))
tcp   LISTEN 0      128          0.0.0.0:22        0.0.0.0:*     users:(("sshd",pid=901,fd=3))
tcp   LISTEN 0      511          0.0.0.0:443       0.0.0.0:*     users:(("nginx",pid=1204,fd=8))
tcp   ESTAB  0      0       192.168.1.10:51342 93.184.216.34:443 users:(("firefox",pid=3388,fd=91))

The flags spell out what you asked for: -t TCP, -u UDP, -l listening sockets, -p owning process, -n numeric ports instead of service names. Reading the output:

  • The udp UNCONN line is a DHCP client waiting on port 68 — UDP has no connection state, so "unconnected" is normal rather than a problem.
  • The two LISTEN lines are services waiting for incoming connections: SSH on port 22, a web server on port 443.
  • The ESTAB line is an outbound connection in progress: this machine's browser connected from local port 51342 to a remote server's port 443.

Notice the asymmetry in that last line. The server side has a fixed, well-known port so clients know where to find it; the client's own port was picked automatically and only needs to be unique on this machine for the life of the connection. Recv-Q and Send-Q show bytes queued but not yet handed to the application or not yet acknowledged — usually zero, and a persistently non-zero Recv-Q on a listening socket is a sign the application isn't accepting connections fast enough.

Your own output will differ in every specific: addresses, ports, PIDs, and which services are running.

Practical scenario: the service is running but unreachable

A Node.js API starts without errors:

Server listening on 127.0.0.1:3000

From the same machine, this works:

curl http://127.0.0.1:3000/health

But from another machine on the LAN, curl http://192.168.1.20:3000/health hangs and then fails. The port number is right and the process is running, so check the socket rather than the port:

sudo ss -tlnp | grep 3000
LISTEN 0 511 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=2291,fd=19))

There's the answer, in the local address rather than the port. The socket is bound to 127.0.0.1, the loopback address covered in IP addressing, and a loopback socket accepts connections only from the same host. The kernel will never hand it traffic that arrived on the LAN interface, no matter what the firewall allows.

Fixing it means binding somewhere else: either the specific LAN address (192.168.1.20) or 0.0.0.0, which means "every IPv4 address on this host."

0.0.0.0 is not a development convenience

Binding to 0.0.0.0 exposes the service on every network the machine is attached to — including, on a cloud instance or a laptop on public Wi-Fi, networks you did not have in mind. Reach for it only alongside a host firewall rule limiting who can connect, real authentication on the service itself, or a reverse proxy in front (a server that terminates client connections and forwards them on, so the application itself is never directly exposed). On a shared or internet-facing host, binding a database or an unauthenticated admin API to 0.0.0.0 is how data gets found by automated scanners within hours.

This is one of the most common backend networking mistakes precisely because the local test passes. Always read the whole socket: protocol, local address, and port. 127.0.0.1:3000 and 0.0.0.0:3000 are not the same listener.

Common mistakes

  • Assuming a port number implies a specific protocol by law rather than convention. Nothing stops someone from running a web server on port 9999 — the well-known assignment of port 80 to HTTP is a strong convention, enforced by client software defaults, not a technical restriction.
  • Forgetting that TCP and UDP port spaces are separate. A TCP service and a UDP service can share the same port number on the same host without conflict, because they're tracked as different sockets.
  • Confusing a listening port with an established connection. A LISTEN state means a service is waiting for connections; ESTAB means an actual conversation is in progress with a specific remote peer.
  • Treating "closed port" and "filtered port" as the same thing. A closed port actively refuses the connection and you get an immediate error; a filtered one is silently dropped by a firewall and you wait for a timeout instead. That difference in how the attempt fails is often the fastest clue to where the problem is.

Practice exercises

  1. Run sudo ss -tulpn on a Linux machine you have access to and list every port it's listening on, along with the process behind each. Then run the same command without sudo and note exactly which column changes.
  2. Explain why a server can accept HTTPS connections from thousands of different clients on port 443 simultaneously without confusing one client's traffic for another's.
  3. A colleague says "port 8080 always means a web server." Explain, using this article's distinction between convention and enforcement, why that claim is only partially true — and what command you'd run to actually check what's on a given port.

That third exercise is a fair description of the whole port system: the numbers carry meaning only because everyone agreed to them, and the agreement is strongest at the low end of the range. The first 1024 ports are where that agreement is strongest of all, backed by decades of client defaults and, on Unix systems, by an actual operating-system restriction on who may use them. Well-known ports is next.

Sources