Unit 4 · Networks · Lesson 7 of 10
Sockets, buffering and backpressure
Step 1: Learn the ideas
- Socket API lifecycle
- Partial reads, partial writes and EAGAIN
- SYN queue, accept queue and listen backlog
- Kernel send and receive buffers and TCP windows
- Reading Recv-Q and Send-Q in ss
- Backpressure in proxies and pipelines
- Bounded queues, admission control and load shedding
- Blocking, readiness-based and completion-based I/O
- Socket options (TCP_NODELAY, SO_KEEPALIVE, buffer sizes)
Why it matters
Backend and infrastructure interviews ask what happens "under load" and "when the client is slow": does memory grow, do connections pile up, does latency explode? The answers live in socket buffers, listen backlogs and how your event loop or thread pool handles partial writes. Networking research uses the same model when it analyzes end-host bottlenecks.
Core ideas
The socket API. A server calls socket, bind and listen, then accept in a loop; a client calls connect. Each accepted connection is a file descriptor. A read returns whatever bytes are available, possibly fewer than requested, and zero means the peer has finished sending. A write copies bytes into the kernel send buffer and, on a non-blocking socket, may accept only part of them.
Queues before accept. The kernel completes TCP handshakes on its own. On Linux, half-open connections wait in a SYN queue and completed ones in an accept queue whose length is bounded by the listen backlog (capped by a system-wide limit). If the application does not call accept fast enough, the accept queue fills and new handshakes are dropped or delayed, so clients see connection timeouts while the process looks healthy. The accept queue length is a direct overload signal.
Buffers and flow control. Each connection has a receive buffer and a send buffer in the kernel. If the application stops reading, the receive buffer fills, the advertised TCP window falls to zero and the sender stops: TCP flow control has carried backpressure to the peer. If the peer stops reading, your send buffer fills and writes block or return EAGAIN. Buffer sizes bound throughput (they must cover the BDP) and memory (connections times buffer size).
Backpressure in applications. A proxy that reads from a fast client and writes to a slow backend must stop reading when its outbound buffer is full; otherwise it buffers without bound in user space and TCP's flow control never engages. Event loops express this by pausing reads above a high-water mark and resuming below a low-water mark. The same principle applies above sockets: bounded queues between stages, admission control, and rejecting early (for example HTTP 429 or 503 with a retry hint) rather than letting latency grow until every request times out.
I/O models. Blocking I/O with a thread per connection is simple but costs memory and context switches at high connection counts. Non-blocking sockets with readiness notification (epoll, kqueue) let one thread serve many connections; completion-based I/O (io_uring) submits operations and collects results. All of them require correct handling of partial reads and writes.
on_readable(client):
if pending_bytes(backend) > HIGH_WATER:
pause_reading(client) # kernel buffer fills, TCP window closes
else:
enqueue(backend, read(client))
on_drained(backend):
if pending_bytes(backend) < LOW_WATER:
resume_reading(client)
Common misconceptions
- "listen(fd, 1024) limits the number of connections." The backlog bounds the queue of completed connections waiting for accept, not the number of concurrent connections.
- "A returned write means the data was sent." It was copied into the kernel buffer; it may not have left the host, let alone reached the peer.
- "Bigger buffers solve overload." They hide it and add latency; overload needs bounded queues and admission control.
- "Non-blocking means fast." It means calls do not wait; you still must handle EAGAIN, partial writes and fairness between connections.
- "A zero-byte read is an error." It signals an orderly shutdown of the peer's sending direction; writing may still be possible.
How to approach it
Write a small framed echo server with non-blocking sockets and test it against a client that sends faster than it reads, watching Recv-Q and Send-Q in ss. In interviews, trace one slow client through the stack (kernel buffer fills, window closes, sender blocks) and say where your service pushes back or sheds load.
Suggested order
- 1Work through Beej's Guide to Network Programming chapters on the system calls and on blocking and non-blocking I/O, writing the client and server examples yourself.
- 2Read the listen(2) page, including its notes on the backlog, and the tcp(7) entries for buffer sizes, TCP_NODELAY and keepalive settings.
- 3Read the Cloudflare post "SYN packet handling in the wild" on how the SYN and accept queues behave and how to observe them.
- 4Re-read OSTEP Ch. 33 (Event-based Concurrency) and connect readiness notification to the backpressure loop in these notes.
- 5Do the framed echo-server project: add high and low water marks, run a client that stops reading, and watch Send-Q and Recv-Q with ss -tn.
- 6Read Google SRE book Ch. 21 (Handling Overload) and decide where your server should reject work instead of queueing it.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
Beej's Guide to Network ProgrammingStart here
beej.us (author site)Practical walkthrough of the sockets API, including non-blocking sockets and poll.
Open(opens in a new tab)Link responded, 5 Oct 2026Linux tcp(7), socket(7) and listen(2) manual pagesStart here
man7.org (Linux man-pages project)Section: listen(2)
Exact Linux semantics of the backlog; continue with tcp(7) and socket(7) for buffer and socket options.
Open(opens in a new tab)Link responded, 5 Oct 2026SYN packet handling in the wild
Cloudflare BlogHow Linux SYN and accept queues behave under load and how to observe them with ss.
Open(opens in a new tab)Link responded, 5 Oct 2026Operating Systems: Three Easy Pieces
University of Wisconsin–Madison (free online book)Section: Ch. 33: Event-based Concurrency
Event loops, non-blocking I/O and why a blocking call stalls an event-driven server.
Open(opens in a new tab)Link responded, 5 Oct 2026Site Reliability Engineering (Google SRE book)
Google (free online edition)Section: Ch. 21: Handling Overload
Admission control, client-side throttling and graceful rejection: backpressure at the service level.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
Implement the echo server twice: blocking with a thread per connection, then non-blocking with poll or epoll. Write a client that sends continuously but never reads, and watch Recv-Q and Send-Q grow with ss -tn until writes block; then add high and low water marks so the server's memory stays bounded. Finally run a listener that never calls accept and watch its accept queue fill with ss -ltn. In CS144, the capacity-limited byte stream of the early checkpoints exercises the same reasoning.
- Build a framed TCP echo server and watch backpressureOpen(opens in a new tab)Not attemptedHands-on project (Beej's Guide as reference)
- Stanford CS144 checkpoints: building TCPOpen(opens in a new tab)Not attemptedStanford CS144
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1What does the backlog argument of listen bound on Linux?
- Question 2A non-blocking write returns fewer bytes than requested. What must the program do?
- Question 3What happens on the wire when a receiver stops reading but keeps the connection open?
- Question 4Why can a proxy run out of memory even though TCP has flow control?
- Question 5When would you set TCP_NODELAY, and what does it trade off?
- Question 6How should a service respond when its internal queues are full?
You’re done when you can
- Implement a non-blocking framed echo server whose memory stays bounded when a client stops reading.
- Explain what Recv-Q and Send-Q mean for listening and for established sockets in ss output.
- Trace a slow consumer from the application buffer through the TCP window to a blocked sender in under three minutes.
- Explain how you would detect accept-queue overflow and what you would do about it.
Finish the lesson
Unlocks next: Network troubleshooting and measurement


