Networking in C · intermediate · ~20 min

Non-blocking sockets — O_NONBLOCK and EAGAIN

Read and write without blocking, and survive the EAGAIN cycle.

Overview

Setting O_NONBLOCK on a socket makes read and write return immediately with EAGAIN/EWOULDBLOCK when they'd otherwise block. You combine this with epoll edge-trigger to drain available data, then return to the loop until the kernel signals more.

Why it matters

A single thread can serve thousands of slow clients only if no one operation blocks. Non-blocking sockets are the precondition; epoll is the dispatcher.

Core concepts

Setting non-blocking. fcntl(fd, F_SETFL, O_NONBLOCK) or pass SOCK_NONBLOCK to socket().

EAGAIN/EWOULDBLOCK. POSIX says they may be different values — always check both. On Linux they're the same.

Partial writes. A non-blocking write may write fewer bytes than requested. Loop. If it returns -1 with EAGAIN, queue the remainder and ask epoll for EPOLLOUT.

Pentester mindset. Slowloris-style attacks send one byte every 30s. A blocking server stalls a worker per attacker; non-blocking + read-timeouts shrugs it off.

Defensive coding habit. ALWAYS handle EAGAIN before treating a -1 as a real error. ALWAYS drain to EAGAIN in edge-triggered mode.

Syntax notes

int flags = fcntl(sock, F_GETFL);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);

Lesson

A non-blocking socket returns from read/write immediately — with data, or with EAGAIN/EWOULDBLOCK meaning 'try again later'. Combined with epoll, this is how you handle thousands of slow clients on one thread.

Code examples

fcntl(sock, F_SETFL, O_NONBLOCK);
ssize_t n = read(sock, buf, sizeof buf);
if (n < 0) {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
        /* No data; come back when epoll says so. */
    }
}

Line by line

for (;;) {
    ssize_t n = read(fd, buf, sizeof buf);
    if (n > 0) { handle(buf, n); continue; }
    if (n == 0) { close(fd); break; }              /* EOF */
    if (errno == EINTR) continue;                  /* signal — retry */
    if (errno == EAGAIN || errno == EWOULDBLOCK)   /* drained — wait for epoll */
        break;
    perror("read"); close(fd); break;              /* real error */
}

Common mistakes

  • Treating EAGAIN as a real error.
  • Forgetting EWOULDBLOCK (it's sometimes a distinct errno value).

Debugging tips

strace -e read,write ./prog shows every short read and EAGAIN. If your server hangs, look for a blocking call you forgot to mark.

Memory safety

Buffer the unsent tail of partial writes. A common bug is to write 100 bytes, get a return of 70, and forget the other 30.

Real-world uses

Every high-perf server. The Linux kernel's networking stack expects this pattern from user-space.

Practice tasks

  1. Set O_NONBLOCK on a socket. 2. Read until EAGAIN. 3. Handle a partial write by queueing the remainder.

Summary

O_NONBLOCK = no blocking. Drain to EAGAIN. Treat EAGAIN as 'try later', not an error.

Practice with these exercises