Необходимость последнего вызова read (2) в Epoll TCP по требованию

Учитывая неблокирующий сокет TCP, если вызов

read(sock, buf, bufLen)

возвращает значение < bufLenбезопасно ли тогда ждать запускаемого по фронту события EPOLLIN? Или я должен позвонить read еще раз, чтобы убедиться, что это ноль или EAGAIN?

В моем тестировании все работает, когда я удаляю последний вызов, я просто хочу знать, гарантировано ли это где-нибудь, или исходным кодом Linux, и могу ли я избавиться от дополнительного вызова.

2 ответа

Решение

На ваш вопрос ответили в man 7 epoll, Как видите, это зависит от типа сокета (пакет / поток):

В9 Нужно ли постоянно читать / записывать файловый дескриптор до EAGAIN при использовании флага EPOLLET (поведение, инициируемое ребром)?

A9 Получение события от epoll_wait(2) должно указывать на то, что такой файловый дескриптор готов к запрошенной операции ввода-вывода. Вы должны считать это готовым, пока следующее (неблокирующее) чтение / запись не даст EAGAIN. Когда и как вы будете использовать файловый дескриптор, зависит только от вас.

Для файлов, ориентированных на пакеты / токены (например, сокет дейтаграммы, терминал в каноническом режиме), единственный способ обнаружить конец пространства ввода / вывода для чтения / записи - это продолжать чтение / запись до EAGAIN.

Для потоково-ориентированных файлов (например, pipe, FIFO, потоковый сокет) условие, что пространство ввода-вывода для чтения / записи исчерпано, также может быть обнаружено путем проверки объема данных, считываемых / записываемых в дескриптор целевого файла. Например, если вы вызываете read(2), запрашивая чтение определенного объема данных, и read(2) возвращает меньшее количество байтов, вы можете быть уверены, что исчерпали пространство чтения ввода-вывода для файлового дескриптора. То же самое верно при написании с использованием write (2). (Избегайте этого последнего метода, если вы не можете гарантировать, что отслеживаемый дескриптор файла всегда ссылается на файл, ориентированный на поток.)

Это "безопасно", поскольку оно не будет падать, но если вы не продолжите звонить read пока не получишь EAGAIN (или ноль, что означает, что другой конец закрыл соединение), вы иногда будете делать неверные предположения о доступности данных. Хуже всего то, что в большинстве случаев он тоже выглядит нормально.

Инициируемый краем в отличие от триггерного уведомления гарантирует только то, что вы получите одно уведомление, если состояние готовности изменилось с момента последнего вызова epoll_wait, даже если остаются данные, которые вы могли бы прочитать.
Уведомление о событиях, запускаемых с помощью Edge, иногда ведет себя немного странно или неинтуитивно в Linux, поэтому оно может делать что-то отличное от того, что вы ожидаете, и, например, давать вам другое уведомление, когда поступает больше данных (так что ваш код, кажется, "работает в любом случае"), но это не то, что гарантировано.
У меня были подобные "сюрпризы" при использовании epoll с eventfd, То, что вы ожидаете, что произойдет в режиме с триггером фронта, это то, что все потоки, которые уже заблокированы, просыпаются (все в одно и то же время и ровно один раз), и каждый вызывает epoll_wait после того, как событие сигнализирует о блокировке, пока событие не будет использовано и снова сигнализировано. Что он на самом деле делает, так это пробуждает первый поток, который называется epoll_wait, И еще раз удивительно, режим, запускаемый по уровням, работает точно так, как вы хотели бы, за исключением того, что вы должны использовать событие, чтобы иметь возможность его подготовить снова, для которого нет правильного способа сделать это (поскольку вы должны сделать это ровно один раз или вы заблокирую в read).

Таким образом, если вы не используете все данные и позже ждете повторного уведомления, вам, возможно, повезет, и это все равно будет "работать", или вы можете ждать довольно долго, возможно, навсегда. Поэтому я рекомендую продолжать читать, пока вы не получите EAGAIN Это единственная надежная вещь, чтобы избежать неожиданностей.

Обратите внимание, что вы можете голодать медленных отправителей, если продолжаете наивно читать. Если у вас очень быстрый отправитель и вы продолжаете читать о быстром отправителе, вы никогда не увидите EAGAIN (по крайней мере, до тех пор, пока другой конец продолжает посылать!), и вы полностью умрете от других отправителей.
Поэтому имеет смысл поместить все готовые дескрипторы в список и прочитать их циклически, удаляя их из списка, когда они возвращаются EAGAIN,

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