Asynchronous & High-Performance I/O

the partial-read / short-write reality

You ask a vending machine for a thousand snacks at once, but it only has 340 in the slot right now, so it gives you 340 and tells you that is all for the moment. It did not fail - it just gave you less than you asked for. This is the everyday reality of read() and write() on streaming descriptors: they may transfer FEWER bytes than you requested, and treating them as all-or-nothing is one of the most common bugs in network code.

Concretely: read(fd, buf, 4096) returns the number of bytes it actually placed, which can be anything from 1 up to 4096 (or 0 at end-of-file, or -1 on error). On a socket, the data may simply not all have arrived yet. write(fd, buf, 4096) likewise returns how many it actually accepted, which can be less than 4096 when the kernel's send buffer is partly full - a 'short write.' The correct discipline is a loop that tracks progress: keep a running offset, on each call read or write the REMAINING bytes from that offset, advance by the count returned, and stop only when you have moved everything (or hit EOF/error). On non-blocking descriptors a short result of EAGAIN means 'no more right now' - you save your position and resume when readiness fires again.

Why it matters: at scale, with non-blocking sockets and partially-filled kernel buffers, short reads and short writes are the NORMAL case, not a rare edge. A server that assumes one write() sends the whole response will silently truncate messages under load; one that assumes one read() returns a whole message will mis-frame data. This is also why buffer management matters so much - you must hold the not-yet-sent tail of a response, and reassemble messages that arrive in pieces across several reads, because the boundaries you care about are yours, not the kernel's.

size_t off = 0; while (off < len) { ssize_t n = write(fd, buf + off, len - off); if (n < 0) { if (errno == EAGAIN) break; /* resume later */ else /* error */ } off += (size_t)n; }

Loop on the remaining bytes from a running offset; never assume one write() flushed the whole buffer.

Short reads/writes are not errors - a positive count smaller than requested is normal, especially on sockets and non-blocking descriptors. The bug is treating the call as all-or-nothing; the fix is always to loop on the remainder and track your own message boundaries, since TCP is a byte stream with no framing.

Also called
short reads and short writespartial I/O短讀與短寫