Part 6 · 1 chapters · ~8 min
Sockets
socket, bind, listen, accept, connect, read and write, the backlog, TCP as a byte stream and message framing, non-blocking sockets, poll, epoll and kqueue, a tiny event loop, SO_REUSEADDR and SO_REUSEPORT, and closing correctly (shutdown, TIME_WAIT).
7
From socket() to an event loop
code
int ls = socket(AF_INET, SOCK_STREAM, 0);
int one = 1; setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); // restart without "address in use"
struct sockaddr_in a = { .sin_family = AF_INET, .sin_port = htons(8080), .sin_addr.s_addr = htonl(INADDR_ANY) };
bind(ls, (struct sockaddr *)&a, sizeof a); listen(ls, 128);
// an event loop with poll: one thread, many connections
struct pollfd fds[1024] = { { .fd = ls, .events = POLLIN } }; int n = 1;
for (;;) {
poll(fds, n, -1);
if (fds[0].revents & POLLIN) { int c = accept(ls, NULL, NULL); fcntl(c, F_SETFL, O_NONBLOCK); fds[n++] = (struct pollfd){ c, POLLIN, 0 }; }
for (int i = 1; i < n; i++) if (fds[i].revents & POLLIN) {
char buf[4096]; ssize_t r = read(fds[i].fd, buf, sizeof buf);
if (r <= 0) { close(fds[i].fd); fds[i--] = fds[--n]; continue; }
handle(fds[i].fd, buf, r);
}
}
// poll is O(n) per call; epoll (Linux) and kqueue (BSD, macOS) report only ready descriptorsA TCP SERVER, CALL BY CALL
the calls under every HTTP framework
swipe the figure sideways, or tap expand for full screen
1/4
listen
socket creates an endpoint, bind attaches an address, listen marks it passive with a backlog queue for completed handshakes.
socket, bind, listenthe backlog queues connections