Networking in C · intermediate · ~10 min
Send and receive bytes over a connected TCP socket, while correctly handling short writes and short reads.
send() and recv(): the socket versions of write/readsend(fd, buf, n, flags) and recv(fd, buf, cap, flags) are the socket-specific siblings of write and read.
0 for flags.MSG_NOSIGNAL, MSG_DONTWAIT, ...) are situational and rarely needed at first.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;
}
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.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 completely or fails. It can succeed partially.recv() == 0 as an error. It is the normal end-of-stream signal (the peer closed the connection).send/recv are the socket versions of write/read; pass 0 for flags in most cases.recv() returning 0 means the peer closed the connection (normal end of stream).recv() returning -1 (with errno set) is a real error.