select(), recv() и EWOULDBLOCK для неблокирующих сокетов

Я хотел бы знать, является ли следующий сценарий реальным?!

  1. select() (RD) на неблокирующем сокете TCP говорит, что сокет готов
  2. следующая функция 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 даже задокументировано, что это может произойти, как я читал.

Смотрите этот вопрос:

Ложное уведомление о готовности к вызову Select System

Хотя мое приложение однопоточное, я заметил, что описанное поведение не редкость в 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().

Это возможно в многопоточной среде, где два потока читают из сокета. Это многопоточное приложение?

Другие вопросы по тегам