recv с MSG_NONBLOCK и MSG_WAITALL

Я хочу использовать системный вызов recv с неблокирующими флагами MSG_NONBLOCK. Но с этим флагом системный вызов может вернуться до того, как будет выполнен полный запрос. Так,

  • я могу добавить флаг MSG_WAITALL? Будет ли он неблокирующим?
  • или как мне переписать блокирующий recv в цикл с неблокирующим recv

person osgx    schedule 31.05.2010    source источник
comment
Предположим, вы хотите сэкономить память пользовательского процесса (для буферизации неполного сообщения), поэтому вы хотите использовать память ядра. Сомневаюсь, что это сработает.   -  person Lorinczy Zsigmond    schedule 17.02.2018


Ответы (3)


Для IPv4 TCP получает по крайней мере в Linux, MSG_WAITALL игнорируется, если указано MSG_NONBLOCK (или файловый дескриптор установлен на неблокирующий).

Из tcp_recvmsg() в net/ipv4/tcp.c в ядре Linux:

if (copied >= target && !sk->sk_backlog.tail)
        break;

if (copied) {
        if (sk->sk_err ||
            sk->sk_state == TCP_CLOSE ||
            (sk->sk_shutdown & RCV_SHUTDOWN) ||
            !timeo ||
            signal_pending(current))
                break;

target в этом приведении устанавливается в запрошенный размер, если указано MSG_DONTWAIT, или меньшее значение (по крайней мере 1), если нет. Функция завершится, если:

  1. Скопировано достаточно байтов
  2. Ошибка сокета
  3. Сокет был закрыт или отключен
  4. timeo равно 0 (сокет настроен на неблокировку)
  5. Ожидается сигнал для процесса

Мне кажется, что это может быть ошибка в Linux, но в любом случае это не будет работать так, как вы хотите. Похоже, что решение dec-vt100 будет, но есть состояние гонки, если вы пытаетесь получить из одного и того же сокета более чем в одном процессе или потоке.
То есть может произойти другой вызов recv() другим потоком/процессом. после того, как ваш поток выполнил просмотр, в результате чего ваш поток заблокировался на втором recv().

person Matt    schedule 11.06.2013
comment
Конечно, timeo, равный нулю, просто означает, что тайм-аут чтения не установлен? - person user207421; 06.02.2020

РЕДАКТИРОВАТЬ:

Plain recv() вернет все, что находится в буфере tcp во время вызова, вплоть до запрошенного количества байтов. MSG_DONTWAIT просто избегает блокировки, если в сокете вообще нет данных, готовых для чтения. MSG_WAITALL запрашивает блокировку до тех пор, пока не будет прочитано все запрошенное количество байтов. Таким образом, вы не получите поведение «все или ничего». В лучшем случае вы должны получить EAGAIN, если нет данных, и заблокировать, пока не будет доступно полное сообщение, в противном случае.

Возможно, вы сможете создать что-то из MSG_PEEK или ioctl() с помощью FIONREAD (если ваша система его поддерживает), что эффективно ведет себя так, как вы хотите, но я не знаю, как вы можете достичь своей цели, просто используя флаги recv().

person Duck    schedule 31.05.2010
comment
NONBLOCK может вернуть только часть требуемого сообщения. Я хочу получить неблокирующий recv формы EAGAIN, если он хочет вернуть только часть msg. Итак, я хочу неблокирующий recv с поведением «все или ничего» - person osgx; 31.05.2010

Это то, что я сделал для той же проблемы, но мне нужно подтверждение того, что это работает так, как ожидалось...

ssize_t recv_allOrNothing(int socket_id, void *buffer, size_t buffer_len, bool block = false)
{
    if(!block)
    {
        ssize_t bytes_received = recv(socket_id, buffer, buffer_len, MSG_DONTWAIT | MSG_PEEK);

        if (bytes_received == -1)
            return -1;

        if ((size_t)bytes_received != buffer_len)
            return 0;
    }

    return recv(socket_id, buffer, buffer_len, MSG_WAITALL);
}
person dec-vt100    schedule 07.06.2011