Что нужно для работы с файловым дескриптором (сокетом) в неблокирующем режиме в Linux?

«Что нужно для работы с файловым дескриптором (сокетом) в неблокирующем режиме в Linux?» — вопрос из категории Сети, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Чтобы работать с файловым дескриптором (чаще всего сокетом) в неблокирующем режиме в Linux, необходимо выполнить два ключевых шага:

  1. Установить флаг O_NONBLOCK для дескриптора.
  2. Обрабатывать специальные коды ошибок EAGAIN или EWOULDBLOCK при операциях ввода-вывода, которые в данном контексте не являются ошибками, а сигнализируют о "попробуй позже".

Установка неблокирующего режима:

#include <fcntl.h>
#include <unistd.h>

// Способ 1: При создании сокета
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);

// Способ 2: Для уже существующего дескриптора с помощью fcntl
int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) return -1;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

Обработка операций:

#include <sys/socket.h>
#include <errno.h>

char buffer[1024];
ssize_t bytes_read = recv(sockfd, buffer, sizeof(buffer), 0);

if (bytes_read == -1) {
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
        // Данных для чтения нет. Дескриптор не готов.
        // Не ошибка, нужно вернуться к циклу поллинга.
    } else {
        // Реальная ошибка (например, соединение разорвано)
        perror("recv failed");
    }
} else if (bytes_read == 0) {
    // Конец файла (EOF) / соединение закрыто корректно
} else {
    // Успешно прочитано `bytes_read` байт
    process_data(buffer, bytes_read);
}

Важное замечание: Работа в неблокирующем режиме сама по себе неэффективна, если постоянно опрашивать дескрипторы в цикле (busy-waiting). Для эффективного ожидания готовности множества дескрипторов необходимо использовать механизмы мультиплексирования ввода-вывода, такие как select, poll или, что предпочтительнее в Linux, epoll. Они сообщают, какие дескрипторы готовы для операций без блокировки.