Networking in C · intermediate · ~10 min

send() and recv() — moving bytes

Send and receive bytes over a connected TCP socket, while correctly handling short writes and short reads.

Lesson

send() and recv(): the socket versions of write/read

send(fd, buf, n, flags) and recv(fd, buf, cap, flags) are the socket-specific siblings of write and read.

  • For most code, pass 0 for flags.
  • The flag bits (MSG_NOSIGNAL, MSG_DONTWAIT, ...) are situational and rarely needed at first.

The real challenge: short sends and short reads

The hard part of TCP is not the API. It is that a single call can move fewer bytes than you asked for. This is called a short send or read.

For example, send(fd, buf, 8, 0) might return 5. You then have to call again with the remaining buf + 5, 3.

The fix is to always loop until every byte is handled:

ssize_t send_all(int fd, const void *buf, size_t n) {
    const char *p = buf;
    while (n > 0) {
        ssize_t k = send(fd, p, n, 0);
        if (k < 0) { if (errno == EINTR) continue; return -1; }
        if (k == 0) return -1;
        p += k; n -= (size_t)k;
    }
    return 0;
}

What return values mean

  • recv() returning 0 means the peer cleanly closed the connection (it sent a FIN). This is normal end-of-stream, not an error.
  • recv() returning -1 (with errno set) is a real error.

Code examples

ssize_t n = recv(fd, buf, cap, 0);
if (n == 0) { /* peer closed */ }
else if (n < 0) { /* error: check errno */ }
else { /* got n bytes — but maybe less than you asked for */ }

Common mistakes

  • Assuming a send() of 1000 bytes either succeeds completely or fails. It can succeed partially.
  • Treating recv() == 0 as an error. It is the normal end-of-stream signal (the peer closed the connection).

Summary

  • send/recv are the socket versions of write/read; pass 0 for flags in most cases.
  • A single call may move fewer bytes than requested, so always loop until all bytes are handled.
  • recv() returning 0 means the peer closed the connection (normal end of stream).
  • recv() returning -1 (with errno set) is a real error.

Practice with these exercises