Right now your laptop is probably holding a dozen conversations at once. A chat app is waiting on a message, three browser tabs are streaming, a mail client is polling, a game is trading position updates, and something in the background is quietly phoning home for updates. All of these ride the same network card, over the same IP address, through the same cable. When a packet arrives, the operating system faces a deceptively simple question: which of these programs does this packet belong to?
The
Imagine an apartment building with a single street address. The postal service (IP) delivers all the mail to the building's front door — that is host-to-host delivery. But inside live dozens of families, and the letters need to reach the right apartment. That last hop, from the front door to a specific mailbox, is process-to-process delivery, and it is exactly what the transport layer provides on top of IP.
93.184.216.34."
It neither knows nor cares which program will read them.
The transport layer wraps each block of application data in its own header and hands the result — a segment — down to IP for delivery. TCP calls its unit a segment; UDP calls its unit a datagram; both are "the transport layer's envelope." The two fields that appear in every transport header, TCP or UDP, are the source port and the destination port. Everything about steering data to the right program hangs off those two numbers.
A port is a 16-bit unsigned integer, so it ranges from
| Range | Name | Who uses it | Examples |
|---|---|---|---|
| 0 – 1023 | Well-known | Standard services; on Unix, binding needs privilege | 80 (HTTP), 443 (HTTPS), 22 (SSH), 53 (DNS), 25 (SMTP) |
| 1024 – 49151 | Registered | Applications that registered a port with IANA | 3306 (MySQL), 5432 (Postgres), 6379 (Redis), 8080 (alt-HTTP) |
| 49152 – 65535 | Ephemeral / dynamic | Temporary source ports the OS auto-assigns to clients | a browser's outgoing connection might use 51314 |
This is why a client rarely picks its own port: when your browser opens a connection to a server on port 443, it lets the OS grab a free ephemeral port — say 51314 — as its own source port. The server port is fixed and famous (443) so that clients know where to knock; the client port is disposable and anonymous, invented fresh for each connection and thrown away when it closes.
Here is the single most important idea on this page, and the one most people get subtly wrong. UDP and TCP demultiplex differently — they use a different number of fields to decide which socket gets an arriving segment.
recvfrom, but that's
for the app to use, not part of demuxing.)
This difference is a direct consequence of what the two protocols are. UDP is connectionless:
a single socket happily receives from the whole world, so "which port" is enough. TCP is
connection-oriented: each connection is a private stream and needs its own socket, so the sender's
coordinates must be part of the key. This is precisely why the
accept() hands you a new socket for every client — each new 4-tuple deserves its
own endpoint.
The demo below builds a tiny demultiplexer. It keeps a table of open sockets — some UDP (keyed by the
2-tuple), some TCP (keyed by the 4-tuple) — and routes a stream of arriving segments to the right one.
Watch how the two TCP connections to the same server port 443 are cleanly separated by
their differing client ports, while every UDP datagram to 53 collapses onto a single
socket.
A tempting mental model is "one port = one connection," as if binding port 443 lets you serve one
client at a time. That is wrong, and if it were true the web could not exist. A busy server holds
tens of thousands of simultaneous TCP connections all on port 443 at once.
They coexist because TCP demultiplexes on the full 4-tuple, not on the destination
port alone. Every connection shares the same
Because UDP keys on just the 2-tuple, a UDP server usually needs only one socket for all its
clients — DNS servers famously answer the whole planet from a single socket bound to port 53. Because
TCP keys on the 4-tuple, a TCP server ends up with one socket per client, all sharing the
listening port. The
recvfrom() over one socket, while a TCP
program calls accept() to mint a fresh socket per connection.
The kernel maintains a table (conceptually a hash map) of open sockets. For every arriving segment it computes the lookup key — the 2-tuple for UDP, the 4-tuple for TCP — and finds the matching entry in roughly constant time. A TCP segment that matches no entry (say, a stray packet for a connection that already closed) typically earns a RST in reply; a UDP datagram for a port nobody is listening on earns an ICMP "port unreachable." The demultiplexer, in other words, also politely tells senders when they've knocked on a door that isn't there.
Everything else the transport layer does — reliability, ordering, flow and congestion control in TCP; cheerful minimalism in UDP — is built on top of this addressing scheme. Multiplexing is the foundation: without a way to name "which program, which conversation," none of the higher services would have anywhere to deliver their carefully-managed bytes.