Networking in C · intermediate · ~10 min

send() and recv() — moving bytes

Send and receive bytes over a connected TCP socket — handling short writes and short reads.

Lesson

send(fd, buf, n, flags) and recv(fd, buf, cap, flags) are the socket-flavoured siblings of write and read. Pass 0 for flags in most cases; the flag bits (MSG_NOSIGNAL, MSG_DONTWAIT, …) are situational.

The hard part of TCP isn't the API — it's that send/recv can be short. send(fd, buf, 8, 0) might return 5; you have to call again with buf + 5, 3. Always loop:

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;
}

recv() returning 0 means the peer cleanly closed the connection (got FIN). recv() returning -1 with errno 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 send() of 1000 bytes either succeeds entirely or fails. It can succeed partially.
  • Treating recv() == 0 as an error. It's the normal end-of-stream signal.

Summary

send/recv may be short — always loop. recv() == 0 is peer-closed; recv() == -1 is an error.

Practice with these exercises