select(), recv() и EWOULDBLOCK для неблокирующих сокетов
Я хотел бы знать, является ли следующий сценарий реальным?!
- select() (RD) на неблокирующем сокете TCP говорит, что сокет готов
- следующая функция recv() вернет EWOULDBLOCK, несмотря на вызов select()
8 ответов
Я знаю об ошибке в популярном рабочем столе, где O_NONBLOCK
TCP-сокеты, особенно те, которые работают через интерфейс обратной связи, иногда могут возвращать EAGAIN
от recv()
после select()
сообщает, что сокет готов к чтению. В моем случае это происходит после того, как другая сторона наполовину закрывает поток отправки.
Для получения дополнительной информации см. Исходный код t_nx.ml
в библиотеке NX моего дистрибутива OCaml Network Application Environment. ( ссылка)
За recv()
ты бы получил EAGAIN
скорее, чем EWOULDBLOCK
и да, это возможно. Так как вы только что проверили с select()
затем произошло одно из двух:
- Что-то еще (другой поток) опустошил входной буфер между
select()
а такжеrecv()
, - Тайм-аут приема был установлен на сокете, и он истек без получения данных.
Это возможно, но только в ситуации, когда у вас есть несколько потоков / процессов, пытающихся читать из одного сокета.
На Linux даже задокументировано, что это может произойти, как я читал.
Смотрите этот вопрос:
Хотя мое приложение однопоточное, я заметил, что описанное поведение не редкость в RHEL5. Оба с сокетами TCP и UDP, которые были установлены в O_NONBLOCK (единственный параметр сокета, который установлен). select() сообщает, что сокет готов, но следующая функция recv() возвращает EAGAIN.
Да, это реально. Вот один из способов, которым это может произойти:
Будущая модификация протокола TCP добавляет возможность для одной стороны "отзывать" информацию, которую она отправила, при условии, что она еще не была получена прикладным уровнем другой стороны. Эта функция согласовывается при подключении. Другая сторона отправляет вам некоторые данные, вы получаете select
удар. Прежде чем позвонить recv
другая сторона "отзывает" данные, используя это новое расширение. Ваш read
получает ошибку "будет блокировать", потому что данные недоступны для чтения.
select
функция представляет собой функцию отчетности о состоянии, которая не сопровождается будущими гарантиями. Предполагая, что удар по select
теперь гарантирует, что последующая операция не будет блокирована, так же недопустимо, как и использование любой другой функции сообщения о состоянии. Это так же плохо, как использовать access
чтобы убедиться, что последующая операция не потерпит неудачу из-за неправильных разрешений или использования statfs
попытаться гарантировать, что последующая запись не потерпит неудачу из-за полного диска.
Если вы не вызовете никакой другой системный вызов между select () и recv () в этом сокете, то recv () никогда не вернет EAGAIN или EWOULDBLOCK.
Я не знаю, что они имеют в виду под recv-timeout, однако стандарт POSIX не упоминает об этом здесь, так что вы можете безопасно вызывать recv().
Это возможно в многопоточной среде, где два потока читают из сокета. Это многопоточное приложение?