Ports & Sockets
Ports & Sockets
LinkedIn Hook
How does your laptop handle 50 browser tabs, a Slack call, a Spotify stream, and a VS Code extension — all downloading data simultaneously from the internet — when you only have one IP address?
The answer is ports.
And the mechanism that ties it all together — the socket — is the most fundamental concept in network programming that most developers never fully understand.
Here is what every software engineer should know:
- Your IP address identifies your machine — your port identifies the service on that machine
- A socket is the combination of IP + port — the complete address of a specific endpoint
- A connection is uniquely identified by 4 values: src IP, src port, dst IP, dst port
- When you visit a website, your OS picks a random ephemeral port (49152–65535) for your side of the connection
- Multiple connections to the same server use the same destination port — differentiated by the ephemeral port on your end
If you have ever written a web server, you have used ports and sockets — probably without fully understanding what happens beneath the surface.
Full lesson → [link]
#Networking #Sockets #BackendEngineering #SystemDesign #InterviewPrep
What You'll Learn
- What a port is and why the range is 0–65535
- The three port categories: well-known, registered, and ephemeral (dynamic)
- The most important well-known ports every developer must memorize
- What a socket is — IP address + port — and how it defines a unique network endpoint
- The 4-tuple that uniquely identifies every TCP connection on the planet
- How multiple connections to the same server port work simultaneously
- What port exhaustion is and when it affects your application
The Analogy That Makes This Click
Think of a city (your IP address) and the buildings in that city (ports).
Your IP address is like a city name — it gets the delivery to the right city. But a city has thousands of buildings. The port number is the building number — it tells the delivery exactly which building inside the city to go to.
A socket is the complete address: city + building = IP + port. 192.168.1.10:443 means "building 443 in the city 192.168.1.10."
When two cities exchange packages, each package has a full return address (your city, your building) and a destination address (their city, their building). That is the 4-tuple: your IP, your port, their IP, their port. No two active deliveries have the same 4-tuple at the same time.
What Is a Port?
A port is a 16-bit unsigned integer — a number from 0 to 65535. At the operating system level, it is a logical channel that allows the OS to route incoming network data to the correct process.
When data arrives at your machine, the OS looks at the destination IP (to confirm it is for this machine) and the destination port (to know which process should receive it). Every process that wants to receive network traffic must bind to a port — it registers with the OS: "I want all incoming traffic on port X."
Incoming packet arrives at 192.168.1.10:80
→ OS checks: which process is bound to port 80?
→ Answer: nginx (web server)
→ OS delivers packet to nginx's socket buffer
Without ports, your machine could only run one network service at a time. Ports allow hundreds of services to run simultaneously, each receiving only its own traffic.
The Three Port Categories
The 65,536 available ports (0–65535) are divided into three ranges by IANA (Internet Assigned Numbers Authority):
| Range | Name | Description |
|---|---|---|
| 0–1023 | Well-Known Ports | Reserved for standard protocols; require root/admin to bind |
| 1024–49151 | Registered Ports | Assigned to specific applications by IANA (voluntary) |
| 49152–65535 | Ephemeral / Dynamic Ports | Used by the OS for client-side connections |
Well-Known Ports — The Must-Memorize List
| Port | Protocol | Transport |
|---|---|---|
| 20 | FTP data | TCP |
| 21 | FTP control | TCP |
| 22 | SSH | TCP |
| 23 | Telnet (insecure, avoid) | TCP |
| 25 | SMTP (email sending) | TCP |
| 53 | DNS | UDP (primarily), TCP (fallback) |
| 67 | DHCP server | UDP |
| 68 | DHCP client | UDP |
| 80 | HTTP | TCP |
| 110 | POP3 (email receiving) | TCP |
| 143 | IMAP (email receiving, synced) | TCP |
| 443 | HTTPS / QUIC (HTTP/3) | TCP / UDP |
| 465 | SMTPS (SMTP over TLS) | TCP |
| 587 | SMTP submission (modern email) | TCP |
| 993 | IMAPS (IMAP over TLS) | TCP |
| 3306 | MySQL | TCP |
| 5432 | PostgreSQL | TCP |
| 6379 | Redis | TCP |
| 27017 | MongoDB | TCP |
Binding to ports 0–1023 requires root privileges on Linux/macOS. This is why web servers bind to port 80 as root and then drop privileges, or use a reverse proxy (nginx) on port 80 that forwards to an unprivileged app on port 3000.
What Is a Socket?
A socket is the combination of an IP address and a port number:
Socket = IP address + Port
Examples:
192.168.1.10:443 — a server's HTTPS endpoint
10.0.0.5:54821 — a client connection (ephemeral port)
127.0.0.1:5432 — PostgreSQL on localhost
0.0.0.0:80 — "listen on port 80 on all interfaces"
A socket is a network endpoint — a specific communication channel. In operating system terms, a socket is a file descriptor that represents one end of a network connection. When you open a socket, you get a descriptor you can read and write — the OS handles translating those reads/writes into network packets.
The 4-Tuple — Unique Connection Identity
A single TCP connection is uniquely identified by exactly four values:
(source IP, source port, destination IP, destination port)
This is called the 4-tuple (or socket pair). No two active TCP connections share the same 4-tuple at the same moment.
Example: Your browser connecting to Google
Your machine: 192.168.1.5 : 54821 (your IP + ephemeral port)
Google's server: 142.250.80.46 : 443 (Google's IP + HTTPS port)
4-tuple: (192.168.1.5, 54821, 142.250.80.46, 443)
If you open a second tab to the same Google server:
4-tuple: (192.168.1.5, 54822, 142.250.80.46, 443)
Different ephemeral port (54822 instead of 54821) → completely different connection. Same destination port 443 — that is fine because the source port is different.
This is how a server handles thousands of simultaneous connections, all to port 443: each connection has a unique source IP:port pair.
Ephemeral Ports — The Client Side
When your application connects to a server, it does not pick a port — the OS picks one automatically. This is the ephemeral port (also called dynamic port).
OS ephemeral port range:
Linux default: 32768–60999 (configurable via /proc/sys/net/ipv4/ip_local_port_range)
IANA standard: 49152–65535
Windows: 49152–65535
The OS cycles through available ephemeral ports for each new outgoing connection. After the connection closes, the port enters TIME_WAIT before being reclaimed (typically 60 seconds).
Port Exhaustion
If your application opens and closes TCP connections very rapidly (no connection pooling), you can exhaust the ephemeral port range. With ~28,000 ports available and 60-second TIME_WAIT per closed connection:
Max connections/second = 28,000 / 60 ≈ 466 new connections/second
If your application exceeds ~466 new outgoing connections per second, you will see EADDRINUSE errors. The fix is connection pooling — reuse existing connections rather than opening new ones per request.
How Multiple Connections Share One Port (Server Side)
This confuses many developers: how can thousands of clients all connect to port 443, when ports uniquely identify connections?
The answer: the server has one listening socket on port 443. For each incoming connection, the OS creates a new connected socket identified by the full 4-tuple:
Server listening socket: 0.0.0.0:443 (accepts any incoming SYN)
Established connections (each is a separate socket):
Connected socket 1: (Client-A-IP:54821, ServerIP:443)
Connected socket 2: (Client-B-IP:61234, ServerIP:443)
Connected socket 3: (Client-A-IP:54822, ServerIP:443) ← same client, second tab
The listening socket stays open and keeps accepting new connections. Each accepted connection produces a new socket that the server uses to communicate with that specific client. Thousands of these can coexist because each has a unique 4-tuple.
Code Example 1 — TCP Socket Server and Client
// TCP server and client demonstrating socket binding, listening, accepting,
// and the 4-tuple that uniquely identifies each connection
const net = require("net");
// === Server ===
const server = net.createServer((socket) => {
// Each incoming connection produces a new socket
// The 4-tuple: client IP + client port + server IP + server port
const clientAddr = `${socket.remoteAddress}:${socket.remotePort}`;
const serverAddr = `${socket.localAddress}:${socket.localPort}`;
console.log(`[Server] New connection from ${clientAddr}`);
console.log(` 4-tuple: (${clientAddr}, ${serverAddr})`);
console.log(` Active connections: ${server.listenerCount("connection")}`);
socket.on("data", (data) => {
console.log(`[Server] Received from ${clientAddr}: "${data.toString().trim()}"`);
socket.write(`Echo: ${data}`);
});
socket.on("close", () => {
console.log(`[Server] Connection closed: ${clientAddr}`);
});
socket.on("error", (err) => {
console.error(`[Server] Socket error (${clientAddr}):`, err.message);
});
});
server.listen(3000, "127.0.0.1", () => {
const addr = server.address();
console.log(`[Server] Listening on ${addr.address}:${addr.port}`);
console.log(` Waiting for connections...`);
});
// === Multiple Clients ===
function createClient(id, message) {
return new Promise((resolve) => {
const client = new net.Socket();
client.connect(3000, "127.0.0.1", () => {
// OS assigned an ephemeral port on the client side
console.log(`[Client ${id}] Connected from port ${client.localPort} → 127.0.0.1:3000`);
client.write(message);
});
client.on("data", (data) => {
console.log(`[Client ${id}] Received: "${data.toString().trim()}"`);
client.destroy();
resolve();
});
});
}
// Simulate three clients connecting simultaneously
setTimeout(async () => {
await Promise.all([
createClient(1, "Hello from client 1"),
createClient(2, "Hello from client 2"),
createClient(3, "Hello from client 3"),
]);
server.close();
console.log("\n[Server] Shut down");
}, 100);
// Each client gets a different ephemeral port from the OS
// All three connect to the same server port 3000
// All three connections coexist via their unique 4-tuples
Code Example 2 — Port Scanner (Educational)
// Educational port scanner — checks which well-known ports are open on localhost
// Demonstrates how sockets work at the connection level
const net = require("net");
const WELL_KNOWN_PORTS = {
22: "SSH",
25: "SMTP",
53: "DNS",
80: "HTTP",
110: "POP3",
143: "IMAP",
443: "HTTPS",
3000: "Node.js dev server",
3306: "MySQL",
5432: "PostgreSQL",
6379: "Redis",
8080: "HTTP alt",
27017: "MongoDB",
};
function checkPort(host, port, timeoutMs = 500) {
return new Promise((resolve) => {
const socket = new net.Socket();
let status = "closed";
socket.setTimeout(timeoutMs);
socket.on("connect", () => {
status = "open"; // TCP handshake succeeded — something is listening
socket.destroy();
});
socket.on("timeout", () => {
status = "filtered"; // No response — firewall may be dropping packets
socket.destroy();
});
socket.on("error", (err) => {
if (err.code === "ECONNREFUSED") {
status = "closed"; // RST received — nothing listening on this port
} else {
status = "error";
}
});
socket.on("close", () => resolve({ port, status, service: WELL_KNOWN_PORTS[port] || "unknown" }));
socket.connect(port, host);
});
}
async function scanPorts(host) {
console.log(`\nScanning ${host} for well-known ports...\n`);
const ports = Object.keys(WELL_KNOWN_PORTS).map(Number);
const results = await Promise.all(ports.map(port => checkPort(host, port)));
const open = results.filter(r => r.status === "open");
const closed = results.filter(r => r.status === "closed");
const filtered = results.filter(r => r.status === "filtered");
console.log("OPEN PORTS:");
if (open.length === 0) {
console.log(" (none)");
} else {
open.forEach(r => console.log(` :${String(r.port).padEnd(6)} ${r.service}`));
}
console.log(`\nCLOSED: ${closed.map(r => r.port).join(", ")}`);
console.log(`FILTERED: ${filtered.length > 0 ? filtered.map(r => r.port).join(", ") : "none"}`);
}
scanPorts("127.0.0.1");
Common Mistakes
Mistake 1 — Thinking one port can only handle one connection
A server listening on port 443 can handle tens of thousands of simultaneous connections. Port 443 is just the destination port — the 4-tuple (src IP, src port, dst IP, dst port) uniquely identifies each connection. The listening socket accepts connections; each accepted connection gets its own socket. Confusing the listening socket with per-connection sockets causes conceptual errors in how servers work.
Mistake 2 — Not using connection pooling in database clients
Every new database connection requires a TCP handshake, authentication, and session setup — then occupies an ephemeral port on the client and a file descriptor on both sides. Opening a new connection per request (a common mistake in serverless functions or improperly configured ORMs) is both slow and resource-intensive. Connection pooling reuses established connections — eliminating the handshake cost and preventing ephemeral port exhaustion.
Mistake 3 — Hardcoding well-known ports without checking conflicts
Ports 3000, 8080, 8000, and 8888 are popular default ports for development servers (React, Django, Rails, Jupyter). If two services try to bind the same port, the second one fails with EADDRINUSE. Always check what is running on a port before assuming it is free (lsof -i :3000 on Mac/Linux, netstat -ano | findstr :3000 on Windows), and configure your applications to use configurable port numbers via environment variables.
Interview Questions
Q: What is the difference between a port and a socket?
A port is a 16-bit number (0–65535) that identifies a specific service or endpoint on a machine. A socket is the combination of an IP address and a port number — 192.168.1.10:443 — forming a complete network endpoint. In programming, a socket is also a file descriptor (an OS-level handle) that represents one end of a network connection. Two sockets — one on each end of a connection — form the connection: (client IP:port, server IP:port).
Q: How can a server accept thousands of connections on the same port?
The server has one listening socket bound to the port. For each incoming connection, the OS creates a new connected socket identified by the unique 4-tuple: (client IP, client port, server IP, server port). Since client IPs and ephemeral ports vary, each 4-tuple is unique even though all connections share the same destination port. The OS multiplexes incoming data to the correct socket based on the 4-tuple.
Q: What is an ephemeral port?
An ephemeral port (also called dynamic or client port) is a temporary port number the OS automatically assigns to the client side of an outgoing connection. When your browser connects to a web server, the OS picks a random available port in the ephemeral range (49152–65535 per IANA; 32768–60999 on Linux by default) for your end of the connection. The server does not need to know your ephemeral port in advance — it learns it when it receives your SYN packet and uses it for all subsequent responses.
Q: What happens when a port is already in use?
If you try to bind to a port that another process is already bound to, the OS returns EADDRINUSE (Address already in use). On TCP servers, there is also the SO_REUSEADDR socket option — when set, it allows binding to a port that is in TIME_WAIT state (from a recently closed connection). This is useful for servers that restart quickly — without SO_REUSEADDR, you would have to wait for TIME_WAIT to expire before restarting.
Q: What is port exhaustion and how do you prevent it?
Port exhaustion happens when all ephemeral ports are in use — typically because a client is opening and closing connections faster than TIME_WAIT releases them. With ~28,000 ephemeral ports and 60-second TIME_WAIT, the limit is roughly 466 new connections/second. Symptoms: EADDRINUSE or connection failures. Prevention: use connection pooling (reuse connections instead of creating new ones per request), or tune TCP_TW_REUSE on Linux to allow safe reuse of TIME_WAIT sockets.
Quick Reference — Cheat Sheet
PORT RANGES
─────────────────────────────────────────────
0–1023 Well-Known (system, require root)
1024–49151 Registered (application-specific)
49152–65535 Ephemeral/Dynamic (OS-assigned for client connections)
SOCKET = IP + PORT
─────────────────────────────────────────────
192.168.1.10:443 → HTTPS server endpoint
127.0.0.1:5432 → PostgreSQL on localhost
0.0.0.0:80 → HTTP on all interfaces
4-TUPLE = UNIQUE CONNECTION
─────────────────────────────────────────────
(src IP, src port, dst IP, dst port)
→ No two active TCP connections share the same 4-tuple
MUST-KNOW PORTS
─────────────────────────────────────────────
22 SSH
25 SMTP
53 DNS (UDP+TCP)
67/68 DHCP
80 HTTP
110 POP3
143 IMAP
443 HTTPS / QUIC
587 SMTP submission
3306 MySQL
5432 PostgreSQL
6379 Redis
27017 MongoDB
EPHEMERAL PORT LIMITS (Linux default: 32768–60999)
─────────────────────────────────────────────
~28,000 ports × (1 / 60s TIME_WAIT) ≈ 466 new connections/sec max
Fix: connection pooling, TCP_TW_REUSE (carefully)
Previous: Lesson 4.3 — TCP vs UDP — The Complete Comparison → Next: Lesson 5.1 — HTTP — How the Web Talks →
This is Lesson 4.4 of the Networking Interview Prep Course — 8 chapters, 32 lessons.