Course outline · 0% complete

0/25 lessons0%

Course overview →

Ports: many doors on one machine

lesson 2-2 · ~9 min · 4/25

One address, many programs

An IP address gets a message to the right machine, but one machine runs many network programs at once: a web server, a database, an SSH login service. A port is a number from 1 to 65535 that picks which program on that machine should receive the message. Address plus port is written 93.184.215.14:443.

Well-known services have standard ports, which is why you rarely type them:

PortService
22SSH (remote terminal)
53DNS
80HTTP (unencrypted web)
443HTTPS (encrypted web)
5432PostgreSQL database

When you visit https://example.com, the browser silently connects to port 443. If a URL includes an explicit port, like http://localhost:3000, that overrides the default. Developers see localhost:3000-style URLs constantly: a development server running on your own machine, listening on port 3000.

Ports earn their keep in debugging: a huge share of "cannot connect" bugs are port bugs. The machine is up and the service is running, but the client is dialing port 5432 while the service listens on 5433, and the connection is refused. Whenever a connection fails, "right machine, right port?" is the first question worth asking.

93.184.215.14one machine:22ssh:443https:5432postgres...address picks the machine, port picks the program
One IP address, many ports. The address gets a message to the machine, and the port number picks which program on it receives the message.

Mapping port numbers to services

A case statement performs the same lookup a scanning tool does when it labels open ports.

for port in 80 443 22 53 8080; do
  case $port in
    22)  svc="ssh" ;;
    53)  svc="dns" ;;
    80)  svc="http" ;;
    443) svc="https" ;;
    *)   svc="unknown" ;;
  esac
  echo "port $port: $svc"
done

Output

port 80: http
port 443: https
port 22: ssh
port 53: dns
port 8080: unknown

Port 8080 falls through to the catch-all, which is honest rather than wrong. 8080 is a common alternative HTTP port by convention, and no rule anywhere makes it so.

That is worth being clear about, because the table of well-known ports is a convention rather than an enforcement. Nothing stops a web server from listening on 5432, and nothing stops a database from listening on 443. The numbers are agreements that keep clients from having to guess.

Which port a browser uses by default

The browser connects to port 443, because https implies it.

Every scheme has a default port. http means 80 and https means 443, so the port is present in the connection even when it is absent from the URL.

There is no such thing as a portless connection. A connection always targets some port, and you only see it written out when it is non-standard, as in localhost:3000.

This is why a development server URL looks different from a production one. Your local app on port 3000 needs the port spelled out, and the same app deployed behind HTTPS does not, which is the same connection made twice with different defaults available.

Splitting host from port

The same trimming trick from lesson 1-2, aimed at a colon.

for addr in db.example.com:5432 cache.example.com:6379 www.example.com:443; do
  host=${addr%%:*}
  port=${addr##*:}
  echo "$host listens on $port"
done

Output

db.example.com listens on 5432
cache.example.com listens on 6379
www.example.com listens on 443

Reading the trims

  • host=${addr%%:*} trims from the first colon to the end, leaving the host.
  • port=${addr##*:} trims everything up to the last colon, leaving the port. The doubled ## is deliberate: it takes the longest match, which is what keeps this correct for an IPv6 address full of colons.
  • Port 6379 is Redis, which is not in the well-known table above and is recognized on sight by anyone who has run a cache. Learning a handful of these numbers is how you read a config file quickly.