Если неблокирующий recv с MSG_PEEK преуспеет, последует ли recv без MSG_PEEK?

Вот упрощенная версия некоторого кода, над которым я работаю:

void
stuff(int fd)
{
    int ret1, ret2;
    char buffer[32];

    ret1 = recv(fd, buffer, 32, MSG_PEEK | MSG_DONTWAIT);

    /* Error handling -- and EAGAIN handling -- would go here.  Bail if
       necessary.  Otherwise, keep going.  */

    /* Can this call to recv fail, setting errno to EAGAIN?  */
    ret2 = recv(fd, buffer, ret1, 0);
}

Если предположить, что первый вызов recv завершается успешно, возвращая значение от 1 до 32, можно ли предположить, что второй вызов также будет успешным? Может ли ret2 быть меньше, чем ret1? В каких случаях?

(Для большей наглядности предположим, что во время второго вызова recv нет других условий ошибки: что сигнал не передан, что он не будет устанавливать ENOMEM и т.д. Также предположим, что никакие другие потоки не будут смотреть на fd.

Я нахожусь в Linux, но MSG_DONTWAIT, я считаю, единственная конкретная Linux. Предположим, что правый fnctl был установлен ранее на других платформах.)

Ответ 1

Стандарт POSIX указывает, что с MSG_PEEK "данные обрабатываются как непрочитанные, а следующая функция recv() или аналогичная функция все равно вернет эти данные". Это означает, что если ret2 равно -1, оно будет таким же, как ret1.

Ответ 2

Вы также должны учитывать возможность того, что между ret1 и ret2 может быть вызван другой вызов recv в другом потоке. Этот другой вызов получит ваши данные, оставив ret2 в конечном итоге без данных или неожиданно меньше данных.

Если ваше приложение не является многопоточным или сконструировано так, что fd используется только этими двумя вызовами, вы можете игнорировать это. Но если это риск, тогда вы должны поместить два вызова в механизм блокировки.

Ответ 3

Второй вызов recv() без MSG_PEEK может завершиться неудачей с EINTR или вернуть неполные данные, поскольку он был прерван сигналом.

Ответ 4

Я не уверен в EAGAIN, но думаю, что EBADF или ECONNRESET возможны.

Ответ 5

Для вашего простого случая последующий recv будет возвращать количество байтов ret1 (если ret1 не был ошибкой). Однако для многопоточной конструкции это может быть не всегда верно.