Ответ
Чтобы работать с файловым дескриптором (чаще всего сокетом) в неблокирующем режиме в 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. Они сообщают, какие дескрипторы готовы для операций без блокировки.
Ответ 18+ 🔞
Ага, слушай сюда, про неблокирующие сокеты. Ну это ж классика, ёпта, как бутерброд с колбасой. Всё просто, как два пальца обоссать, но если не понять суть — будет тебе хитрая жопа на ровном месте.
Вот представь: твоя программа, как официант в забитом кабаке. Если он блокирующий, то подойдёт к одному столику, стоит и ждёт, пока мужик там десять минут решает, брать ему сало или нет. А остальные в это время орут «Эй, пива давай!» и дохуя злые. Так работать нельзя, терпения ноль ебать.
Задача — сделать так, чтобы официант носился как угорелый, ко всем подбегал, и если кто-то не готов (типа «я ещё думаю»), то он сразу летит к следующему. Вот это и есть неблокирующий режим.
Как это делается, блядь?
Первое — надо сказать системе: «Слушай, чувак, я не буду тут стоять и ждать, если ты мне ничего не дашь». Делается это флагом O_NONBLOCK.
// Вариант раз — сразу при рождении сокета, чтоб с пелёнок неблокирующий
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
// Вариант два — если сокет уже живёт своей жизнью, а ты вдруг решил
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); // Прилепили флажок
}
Всё, сокет теперь полупидор — он не блокируется. Ты вызываешь recv(), а данных нет. Раньше бы программа встала колом и ждала, пока кто-то что-то пришлёт. А теперь что? Теперь он сразу тебе вернёт управление и скажет: «Ни хуя себе, братан, данных нет».
Но как он это скажет? А вот через магические коды ошибок EAGAIN или EWOULDBLOCK. Это не ошибки на самом деле, это такие вежливые отмазки: «Извини, чувак, я бы рад, но пока не готов, зайди позже».
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) {
// Клиент вежливо отсоединился. Ну и хуй с ним.
} else {
// Ура, прочитали! Работай с данными.
process_data(buffer, bytes_read);
}
И вот тут главный подвох, на котором все спотыкаются, я тебя предупреждаю. Если ты просто в цикле будешь дергать recv() на сотне сокетов и каждый раз получать EAGAIN — твоя программа сожрёт овердохуища процессорного времени, просто тупо проверяя, не появилось ли чего. Это как официант, который каждую секунду подбегает к каждому столику и спрашивает: «Ну чё, решил? Нет? А сейчас?»
Так делать — пиздопроебибна идея. Для этого умные люди придумали системы оповещения: select, poll и король всех — epoll. Они сидят, следят за всеми твоими сокетами, и как только какой-то из них готов к чтению или записи — они тебе кричат: «Эй, иди к третьему столику, там мужик уже заказ сделал!». И ты идешь и работаешь только с теми, кто готов. Эффективность — ебать копать.
Короче, алгоритм такой:
- Включи неблокирующий режим.
- Смирись с тем, что
EAGAIN— это норма жизни, а не ошибка. - НИКОГДА не работай без
epoll/poll/select, иначе твой ЦПУ сгорит, а программа будет тормозить как хуй в пальто. - Профит.
Всё, понял? Работай.