0Pricing
C Academy · Урок

Обработка нескольких клиентов

select и poll

«Обработка нескольких клиентов» — бесплатный урок C Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C Academy содержит 4 уроков всего.

Проблема параллельного выполнения

Простой сервер обрабатывает по одному клиенту за раз. Если клиент работает медленно, все остальные ждут. Чтобы обслуживать много клиентов одновременно, нужна другая модель.

Подходы к параллельному выполнению

Три распространённые стратегии:

  • отдельный поток или процесс для каждого клиента: просто, но при большом масштабе требует много ресурсов
  • мультиплексирование ввода-вывода с помощью select или poll: один поток отслеживает множество сокетов
  • циклы обработки событий с помощью epoll/kqueue: подходят для тысяч соединений

Что делает select

select() отслеживает набор файловых дескрипторов и одним блокирующим вызовом сообщает, какие из них готовы к чтению или записи либо содержат ошибки. Это позволяет одному потоку обслуживать множество сокетов.

Основы fd_set

select использует битовую маску fd_set. Управляйте ею с помощью макросов: FD_ZERO, FD_SET, FD_CLR и 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 */
}

Вызов select

select получает наибольший fd плюс единицу и изменяет набор непосредственно, отмечая готовые дескрипторы. Поскольку набор разрушается, перед каждым вызовом его нужно создавать заново.

#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 */
}

Цикл сервера с select

Шаблон выглядит так: создайте набор заново, вызовите select, затем для каждого готового fd либо примите нового клиента (если это дескриптор прослушивания), либо прочитайте данные (если это клиент).

#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 */
        }
    }
}

Отслеживание клиентов

Приняв новое соединение, добавьте его fd в основной набор и обновите maxfd. Когда клиент отключается (recv возвращает 0), вызовите для него close и удалите его из набора с помощью FD_CLR.

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

Ограничения select

У select есть недостатки:

  • ограничение в FD_SETUP дескрипторов (часто 1024)
  • сканирование всех fd за O(n) при каждом вызове
  • набор нужно создавать заново на каждой итерации

Для большого числа соединений poll или epoll масштабируются лучше.

Возможности poll

poll() принимает массив struct pollfd вместо фиксированной битовой маски, поэтому не ограничен 1024 дескрипторами и не разрушает входные данные при каждом вызове.

#include <poll.h>

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

Вызов poll

poll блокируется, пока хотя бы один fd не будет готов (или пока не истечёт время ожидания), а затем устанавливает revents для каждой готовой записи.

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

Масштабирование: epoll

Для тысяч соединений Linux-механизм epoll (а также BSD-механизм kqueue) возвращает только готовые дескрипторы, обеспечивая O(1) на событие и избегая полного сканирования, которое выполняют select и poll.

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете обслуживание нескольких клиентов.

Повторение

Вы научились обслуживать множество клиентов одним потоком.

  • select отслеживает множество fd; управляйте ими с помощью макросов FD_
  • Создавайте набор заново в каждом цикле, поскольку select его разрушает
  • Добавляйте принятых клиентов в набор и удаляйте их при отключении
  • poll не ограничен 1024 дескрипторами; epoll масштабируется до тысяч соединений

Часто задаваемые вопросы

Урок «Обработка нескольких клиентов» бесплатный?

Да — полный текст урока «Обработка нескольких клиентов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C Academy, подпишись на CoddyKit PRO. Курс C Academy содержит 4 уроков всего.

Чему я научусь в уроке «Обработка нескольких клиентов»?

select и poll Ты практикуешь C Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C Academy?

Предыдущий опыт не требуется. C Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Обработка нескольких клиентов»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C Academy?

Да. Каждый урок C Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Обзор сокетов
  2. TCP-сервер
  3. TCP-клиент
  4. Обработка нескольких клиентов
← Назад к C Academy