recv с MSG_NONBLOCK и MSG_WAITALL - PullRequest
       12

recv с MSG_NONBLOCK и MSG_WAITALL

8 голосов
/ 31 мая 2010

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

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

Ответы [ 3 ]

4 голосов
/ 08 июня 2011

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

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);
}
3 голосов
/ 11 июня 2013

Для получения 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 ().

3 голосов
/ 31 мая 2010

EDIT:

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

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

...