Sockets & Ports (Network Programming Basics)
Explore kernel network primitives: File descriptors, socket bindings, ephemeral port allocations, non-blocking I/O, and the C10K/C1000K concurrent connection problem.
Socket 5-Tuple Connection Identification & epoll 🔌
How operating systems uniquely identify and route millions of simultaneous connections.
01.1. What is a Network Socket?
In UNIX-like operating systems, "everything is a file." A socket is an operating system file descriptor (integer handle) representing a communication endpoint.
When a server starts:
socket(): Creates the socket descriptor.bind(): Associates the socket with an IP address and Port (e.g.0.0.0.0:8080).listen(): Puts socket in passive listening mode with a backlog queue.accept(): Blocks/waits until a client connects, returning a new socket descriptor dedicated to that specific client connection.
package main
import (
"fmt"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
defer listener.Close()
fmt.Println("🚀 Server listening on port 8080...")
for {
conn, err := listener.Accept()
if err != nil {
continue
}
// Spawn lightweight goroutine per socket
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello Socket!"))
_ = n
}02.2. The Myth of the 65,535 Connection Limit
A common misconception is that a web server can only accept 65,535 simultaneous connections because ports are 16-bit integers.
Reality: The 65,535 limit applies to outgoing connections sharing a single source IP. Because a connection is identified by the full 5-Tuple (Protocol, Src IP, Src Port, Dst IP, Dst Port), a single server listening on port 443 can easily hold millions of concurrent open connections, constrained only by available RAM (socket buffer memory) and OS file descriptor limits (ulimit -n).
03.3. Solving C10K: epoll, kqueue, and io_uring
The C10K Problem (Dan Kegel, 1999) asked: how can a single server handle 10,000 concurrent client connections efficiently? The naive approach — 1 OS thread per connection — fails because:
- Each thread consumes 1–8MB of stack memory (10,000 threads = 8–80 GB RAM)
- Context-switching 10,000 OS threads generates massive scheduler overhead
Solution: I/O Multiplexing
select()/poll()(old): Scan all file descriptors linearly — O(N) per wakeup. Becomes slow at thousands of connections.epoll(Linux 2.6+): Kernel maintains an event-driven watch list. Only notifies the application about FDs with actual data — O(1) per event. Nginx, Node.js, and Redis all use epoll internally.kqueue(BSD/macOS): Equivalent to epoll, used by Nginx and Go's runtime on macOS.io_uring(Linux 5.1+): Next-generation async I/O interface using shared memory ring buffers between kernel and userspace, eliminating syscall overhead entirely. Dramatically higher throughput for disk + network I/O at extreme scales.
04.4. SO_REUSEPORT — Scaling the Accept Loop
SO_REUSEPORT (Linux 3.9+) is a socket option that allows multiple processes or threads to bind to the same port simultaneously. The kernel load-balances incoming connections across all listening sockets using a hash of the 4-tuple (src IP, src port, dst IP, dst port).
Before SO_REUSEPORT: A single process ran the accept loop, becoming a bottleneck on multi-core CPUs.
After SO_REUSEPORT: Each CPU core can run its own accept loop on the same port, eliminating the accept lock contention bottleneck. Nginx uses SO_REUSEPORT to scale across all CPU cores.
Configuration in Nginx: listen 443 ssl reuseport;
This can increase connection throughput by 3-4x on high-core-count machines.
⚖️Architectural Trade-offs & Production Realities
Architectural Advantages
- Low-level socket control unlocks high-performance event-driven architectures (epoll/kqueue)
- SO_REUSEPORT scales accept loop across all CPU cores
Trade-offs & Constraints
- Requires careful management of file descriptors, buffer sizing, and non-blocking I/O event loops
- io_uring requires Linux 5.1+ — not available on older or non-Linux systems
WhatsApp famously optimized FreeBSD kernel socket buffers and Erlang lightweight actors to maintain over 2.8 million active TCP chat sockets on a single physical server.
🎯 Staff+ Engineering Takeaways
- A socket is an OS file descriptor representing a network endpoint.
- Connections are identified by a 5-Tuple, allowing millions of concurrent connections on port 443.
- Use I/O multiplexing (epoll) to handle massive concurrency without thread explosion.
- SO_REUSEPORT allows multiple processes to accept on the same port, scaling across CPU cores.
- io_uring is the next-generation Linux async I/O interface, eliminating syscall overhead for extreme performance.
Topic Knowledge Assessment 🧠
Step through 1 scenario question to test your staff-level grasp.
Can a single web server listening on port 443 handle more than 65,535 concurrent client connections?
How clear and staff-actionable was this system breakdown?