Sockets & Ports Lab (Interactive)
Scale to millions of connections across thread-per-conn, select(), epoll, and io_uring while watching RAM. Compute kernel memory and scan costs for I/O multiplexing models, explore the connection 5-tuple, and toggle SO_REUSEPORT across cores.
Sockets, Ports & the C10K Concurrency Bench
Size RAM and syscall behavior for connection models, and prove the 5-tuple breaks the 65,535-port myth.
Nginx, Node.js, Redis use this: 1 thread handles hundreds of thousands of sockets
Same server port, unique tuples → every connection is distinct; 65,535 only caps one client IP’s outgoing fan-out (32,768 ephemeral ports).
Socket lifecycle syscalls
- socket() → fd 3 (everything is a file)
- bind(fd, 0.0.0.0:443) → attach IP + port
- listen(fd, backlog=1024) → passive mode + SYN queue
- accept(fd) → NEW fd per client (fd 7, 8, 9…)
- epoll_ctl(ADD, ready-fd) → kernel watch list
- epoll_wait() → wake only for readable fds (the O(1) magic)
How It Works Under the Hood
A socket is just a file descriptor bound to an IP and port; accept() returns a new descriptor per client, and the kernel identifies every connection by its 5-tuple — protocol, source IP, source port, destination IP, destination port. That is the myth-buster behind the 65,535 limit: it applies only to one client IP’s outgoing ephemeral ports, so one server on port 443 can hold millions of sockets. The C10K problem killed thread-per-connection (8MB stacks, scheduler thrash); epoll’s O(1) ready-list notifications power Nginx, Node.js, and Redis, while SO_REUSEPORT spreads accept loops across cores and io_uring removes syscalls entirely.
Core Architectural Principles
- socket() → bind() → listen(backlog) → accept() lifecycle returns a dedicated fd per connection.
- select()/poll() scan all fds O(N) per wakeup; epoll notifies only ready fds in O(1).
- SO_REUSEPORT lets N worker threads own separate port-443 listen queues, scaling accepts per CPU core.
For chat or long-connection systems, answer C10K concretely: event-driven I/O (epoll/kqueue), non-blocking sockets, per-connection fd cost in kernel buffers, and WhatsApp’s 2.8M connections per server as precedent. Then explain fd limits (ulimit), memory per idle socket, and SO_REUSEPORT for multi-core accept scaling — this sequence separates staff-level candidates from buzzword users.
Raw socket control and event loops unlock million-connection servers but demand careful fd, buffer, and non-blocking I/O management.