Networking in C · intermediate · ~20 min
Read and write without blocking, and survive the EAGAIN cycle.
Setting O_NONBLOCK on a socket changes how read and write behave. Instead of waiting (blocking) when an operation cannot complete right away, they return immediately with the error EAGAIN (or EWOULDBLOCK).
This flag pairs naturally with epoll in edge-triggered mode:
A single thread can serve thousands of slow clients only if no operation ever blocks.
Non-blocking sockets are the precondition. Epoll is the dispatcher that tells you which socket is ready.
Two ways:
fcntl(fd, F_SETFL, O_NONBLOCK) on an existing socket.SOCK_NONBLOCK when you create the socket.These errors mean "no data right now, try again later" — not a real failure.
POSIX allows them to be two different values, so always check both. On Linux they happen to be identical.
A non-blocking write may write fewer bytes than you asked for.
write returns -1 with EAGAIN, the kernel buffer is full. Queue the leftover bytes and ask epoll to notify you with EPOLLOUT (socket writable again).Slowloris-style attacks send one byte every 30 seconds to tie up server resources.
EAGAIN before treating a -1 return as a real error.EAGAIN when using edge-triggered mode.Read the current flags, then add O_NONBLOCK without clearing the others:
int flags = fcntl(sock, F_GETFL);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
A non-blocking socket returns from read or write immediately. You get one of two results:
EAGAIN / EWOULDBLOCK, meaning "try again later."Combined with epoll, this is how you serve thousands of slow clients on a single thread.
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. */
}
}
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 */
}
EAGAIN as a real error instead of "try again later."EWOULDBLOCK — on some systems it is a distinct errno value.Use strace to watch the actual system calls:
strace -e read,write ./prog
This shows every short read and every EAGAIN.
If your server hangs, look for a blocking call you forgot to mark as non-blocking.
Always buffer the unsent tail of a partial write.
A common bug: you call write for 100 bytes, get a return value of 70, and forget the remaining 30. Those bytes are lost unless you keep them and send them later.
Every high-performance server uses this pattern. The Linux kernel's networking stack is designed around it and expects user-space code to follow it.
O_NONBLOCK on a socket.EAGAIN.O_NONBLOCK makes read/write return immediately instead of blocking.EAGAIN/EWOULDBLOCK as "try again later," never as a real error.EAGAIN.