0Pricing
C Academy · Lesson

Handling Multiple Clients

select and poll.

Handling Multiple Clients is a free C Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Concurrency Problem

A simple server handles one client at a time. If a client is slow, everyone else waits. To serve many clients concurrently, you need a different model.

Approaches to Concurrency

Three common strategies:

  • Thread/process per client: simple but heavy at scale
  • I/O multiplexing with select or poll: one thread watches many sockets
  • Event loops with epoll/kqueue: scales to thousands

What select Does

select() watches a set of file descriptors and tells you which are ready for reading, writing, or have errors, all in a single blocking call. This lets one thread serve many sockets.

fd_set Basics

select uses an fd_set bitmask. Manage it with macros: FD_ZERO, FD_SET, FD_CLR, and FD_ISSET.

#include <sys/select.h>

void setup_set(fd_set *set, int listen_fd) {
    FD_ZERO(set);          /* clear all */
    FD_SET(listen_fd, set); /* watch the listener */
}

Calling select

select needs the highest fd plus one, and modifies the set in place to mark ready descriptors. Because it is destructive, you rebuild the set before each call.

#include <sys/select.h>
#include <stdio.h>

int wait_ready(int maxfd, fd_set *read_set) {
    int n = select(maxfd + 1, read_set, NULL, NULL, NULL);
    if (n < 0) perror("select");
    return n; /* number of ready fds */
}

The select Server Loop

The pattern: rebuild the set, call select, then for each ready fd either accept a new client (if it is the listener) or read data (if it is a client).

#include <sys/select.h>

void loop_skeleton(int listen_fd, fd_set *master, int maxfd) {
    fd_set work;
    for (;;) {
        work = *master;           /* copy, select destroys it */
        select(maxfd + 1, &work, NULL, NULL, NULL);
        for (int fd = 0; fd <= maxfd; fd++) {
            if (!FD_ISSET(fd, &work)) continue;
            /* fd == listen_fd -> accept; else -> recv */
        }
    }
}

Tracking Clients

When you accept a new connection, add its fd to the master set and update maxfd. When a client disconnects (recv returns 0), close it and FD_CLR it from the set.

#include <sys/select.h>
#include <unistd.h>

void add_client(fd_set *master, int *maxfd, int conn) {
    FD_SET(conn, master);
    if (conn > *maxfd) *maxfd = conn;
}
void drop_client(fd_set *master, int conn) {
    close(conn);
    FD_CLR(conn, master);
}

Limits of select

select has drawbacks:

  • Capped at FD_SETUP descriptors (often 1024)
  • O(n) scan of all fds every call
  • Set rebuilt each iteration

For many connections, poll or epoll scale better.

What poll Offers

poll() takes an array of struct pollfd instead of a fixed bitmask, so it has no 1024-descriptor limit and does not destroy the input on each call.

#include <poll.h>

void setup_poll(struct pollfd *pfd, int listen_fd) {
    pfd[0].fd = listen_fd;
    pfd[0].events = POLLIN; /* notify when readable */
}

Calling poll

poll blocks until at least one fd is ready (or the timeout fires), then sets revents on each ready entry.

#include <poll.h>
#include <stdio.h>

int poll_ready(struct pollfd *pfds, int count) {
    int n = poll(pfds, count, -1); /* -1 = block forever */
    if (n < 0) perror("poll");
    return n;
}

Scaling Up: epoll

For thousands of connections, Linux's epoll (and BSD's kqueue) deliver only the ready descriptors in O(1) per event, avoiding the full scan that select and poll perform.

Quick Check

Test your understanding of handling multiple clients.

Recap

You learned to serve many clients with one thread.

  • select watches many fds; manage with FD_ macros
  • Rebuild the set each loop because select is destructive
  • Add accepted clients to the set; drop on disconnect
  • poll avoids the 1024 limit; epoll scales to thousands

Frequently asked questions

Is the “Handling Multiple Clients” lesson free?

Yes — the full text of “Handling Multiple Clients” is free to read here on the web, and the C Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C Academy course, upgrade to CoddyKit PRO.

What will I learn in “Handling Multiple Clients”?

select and poll. You practise C Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C Academy?

No prior experience is required. C Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Handling Multiple Clients” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C Academy lesson?

Yes. Every C Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Sockets Overview
  2. TCP Server
  3. TCP Client
  4. Handling Multiple Clients
← Back to C Academy