Ответ
Чтобы работать с файловым дескриптором (чаще всего сокетом) в неблокирующем режиме в Linux, необходимо выполнить два ключевых шага:
- Установить флаг
O_NONBLOCKдля дескриптора. - Обрабатывать специальные коды ошибок
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. Они сообщают, какие дескрипторы готовы для операций без блокировки.