Networking in C · intermediate · ~10 min
Send and receive bytes over a connected TCP socket — handling short writes and short reads.
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.
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 */ }
send() of 1000 bytes either succeeds entirely or fails. It can succeed partially.recv() == 0 as an error. It's the normal end-of-stream signal.send/recv may be short — always loop. recv() == 0 is peer-closed; recv() == -1 is an error.