Home/Labs/Sockets & the C10K Problem
All 280 Labs
INTERACTIVE LAB🔌

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

Server 203.0.113.10:443 — distinct 5-tuples
(TCP, 198.51.100.55:46626, 203.0.113.10:443) → fd 8
(TCP, 198.51.100.110:60480, 203.0.113.10:443) → fd 9
(TCP, 198.51.100.166:41571, 203.0.113.10:443) → fd 10

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 memory
20 MB
Per-event scan
O(1) — kernel notifies only ready fd
Accept hot core load
10%
fd limit (ulimit -n)
65,536
10,000 connections under epoll / kqueue: 20480 MB of kernel socket state, O(1) — kernel notifies only ready fd. WhatsApp famously sustained 2.8M chat sockets on one server with tuned buffers and actors.

Socket lifecycle syscalls

  1. socket() → fd 3 (everything is a file)
  2. bind(fd, 0.0.0.0:443) → attach IP + port
  3. listen(fd, backlog=1024) → passive mode + SYN queue
  4. accept(fd) → NEW fd per client (fd 7, 8, 9…)
  5. epoll_ctl(ADD, ready-fd) → kernel watch list
  6. 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.
Interview Round Script

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.

Key Trade-Offs

Raw socket control and event loops unlock million-connection servers but demand careful fd, buffer, and non-blocking I/O management.

Related Curriculum Chapter

Sockets & Ports (Network Programming Basics)

Read Full Chapter Blueprint

Explore More Interactive Labs

View All 280 Labs