Случай, когда блокировка recv () возвращает меньше запрошенных байтов

В справочной странице библиотечной функции recv() упоминается, что:

Возвращает количество полученных байтов. Обычно он возвращает любые доступные данные вплоть до запрашиваемой суммы, а не ожидает получения всей запрашиваемой суммы.

Если мы используем блокирующий вызов recv() и запрашиваем 100 байтов:

recv(sockDesc, buffer, size, 0); /* Where size is 100. */

и только 50 байтов отправляются сервером, то это recv() блокируется до тех пор, пока не будет 100 байтов. доступно или получит 50 байтов.

Сценарий может быть следующим:

  • сбой сервера после отправки только 50 байтов

  • неправильная схема протокола, когда сервер отправляет только 50 байтов в то время как клиент ожидает 100, а сервер также ожидает ответа клиента (т. е. соединение закрытия сокета не было инициировано сервером, на котором будет возвращено recv)

Меня интересует платформа Linux / Solaris , У меня нет среды разработки, чтобы проверить это сам.

8
задан alk 9 May 2015 в 08:41
поделиться

3 ответа

recv вернет, если во внутренних буферах есть данные, которые нужно вернуть. Он не будет ждать, пока наберется 100 байтов, если вы запросите 100 байтов.

Если вы отправляете 100-байтовые «сообщения», помните, что TCP не предоставляет сообщения, это просто поток. Если вы имеете дело с сообщениями приложения, вам необходимо обрабатывать это на уровне приложения, поскольку TCP этого не сделает.

Существует множество условий, при которых вызов send () размером 100 байт не может быть полностью прочитан на другом конце только с одним вызовом recv при вызове recv (..., 100); вот только несколько примеров:

  • Передающий стек TCP решил объединить вместе 15 вызовов записи, и MTU оказалось 1460, что - в зависимости от времени поступления данных может привести к тому, что клиенты первыми будут 14 вызовы для выборки 100 байт и 15. вызов для выборки 60 байт - последние 40 байт появятся при следующем вызове recv (). (Но если вы вызываете recv с буфером 100, вы можете получить последние 40 байтов «сообщения» предыдущего приложения и первые 60 байтов следующего сообщения)

  • Буферы отправителя заполнены, возможно, программа чтения работает медленно , или сеть перегружена.В какой-то момент данные могут пройти, и при опустошении буферов последний фрагмент данных не будет кратен 100.

  • Буферы приемника заполнены, в то время как ваше приложение получает эти данные, последний фрагмент, который он извлекает. является частичным, поскольку целые 100 байтов этого сообщения не помещаются в буферы.

Многие из этих сценариев довольно сложно протестировать, особенно в локальной сети, где у вас может не быть большой перегрузки или потери пакетов - все может измениться, когда вы увеличиваете и уменьшаете скорость отправки / создания сообщений.

В любом случае. Если вы хотите прочитать 100 байт из сокета, используйте что-то вроде

int
readn(int f, void *av, int n)
{
    char *a;
    int m, t;

    a = av;
    t = 0;
    while(t < n){
        m = read(f, a+t, n-t);
        if(m <= 0){
            if(t == 0)
                return m;
            break;
        }
        t += m;
    }
    return t;
}

...

if(readn(mysocket,buffer,BUFFER_SZ) != BUFFER_SZ) {
  //something really bad is going on.

}
17
ответ дан 5 December 2019 в 06:23
поделиться

Поведение определяется двумя вещами. Отметка о низком уровне восстановления и то, передаете ли вы флаг MSG_WAITALL . Если вы передадите этот флаг, вызов будет заблокирован до тех пор, пока не будет получено запрошенное количество байтов, даже если сервер выйдет из строя. В противном случае он возвращается, как только в приемном буфере сокета становится доступно хотя бы SO_RCVLOWAT байт.

SO_RCVLOWAT

Устанавливает минимальное количество байтов для процесса для операций ввода через сокет. Значение по умолчанию для SO_RCVLOWAT - 1. Если для SO_RCVLOWAT установлено большее значение, блокирующие вызовы приема обычно ждут, пока они не получат меньшее из нижнего значения отметки уровня воды или запрошенной суммы. (Они могут возвращать меньше, чем нижняя отметка, если возникает ошибка , обнаружен сигнал или тип данных, следующих в очереди приема отличается от возвращаемого, например, данные вне канала). Эта опция принимает значение типа int. Обратите внимание, что не все реализации позволяют установить этот параметр .

7
ответ дан 5 December 2019 в 06:23
поделиться

Если вы внимательно прочитали цитату, наиболее распространенный сценарий:

  • сокет получает данные. На эти 100 байтов потребуется время.
  • выполняется вызов recv ().
    • Если в буфере больше 0 байтов, recv () возвращает то, что доступно, и не ждет.
    • Пока доступно 0 байтов, он блокируется, и степень детализации системы потоков определяет, как долго это будет длиться.
2
ответ дан 5 December 2019 в 06:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: