2014-12-01 5 views
1

Я пытаюсь последовательно читать UDP-грамм данных. Но проблема возникла во время второй операции чтения. Вы можете увидеть ниже, где я пытаюсь прочитать первый раз и потерял 12 байт пакета из буфера приема UDP.Странная потеря пакетов UDP

Snippest Code: 
sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); 

bytesInBuffer = 0; 
if (-1 == ioctl(sock, FIONREAD, &bytesInBuffer)) 
{ 
    printf("%s:%d, Fail to read bytes in buffer\n", __func__, __LINE__); 
    //If failure on ioctl then continue with select and read functionality 
    bytesInBuffer = 0; 
} 
printf("%s:%d, bytesInBuffer: %d\n", __func__, __LINE__, bytesInBuffer); 

errno = 0; 
/* Now recv as it will not block */ 
i32RetVal = recvfrom(sock, buffer, 40, MSG_NOSIGNAL | MSG_DONTWAIT, NULL, NULL); 
printf("%s:%d, i32RetVal: %d\n", __func__, __LINE__, i32RetVal); 

if (0 > i32RetVal) 
{ 
    printf("%s:%d, Recv failed with status: %d, Err: %d, SErr: %s\n", __func__, __LINE__, status, errno, strerror(errno)); 
    status = -1; 
    break; 
} 
else if (0 == i32RetVal) 
{ 
    /* other side closed its send pipe */ 
    status = -1; 
    printf("%s:%d, Recv failed as other side closed pipe\n", __func__, __LINE__); 
    break; 
} 

bytesInBuffer = 0; 
if (-1 == ioctl(sock, FIONREAD, &bytesInBuffer)) 
{ 
    printf("%s:%d, Fail to read bytes in buffer\n", __func__, __LINE__); 
    //If failure on ioctl then continue with select and read functionality 
    bytesInBuffer = 0; 
} 
printf("%s:%d, bytesInBuffer: %d\n", __func__, __LINE__, bytesInBuffer); 

Output: 
Recv_from:304, bytesInBuffer: 52 
Recv_from:309, i32RetVal: 40 
Recv_from:332, bytesInBuffer: 0 

Вопрос: Почему 12 байт из буфера чтения были отброшены?

Любая помощь будет оценена по достоинству. :)

+0

Спасибо за разъяснение. :) И еще один вопрос, если у меня есть заголовок и тело для передачи через UDP, тогда мне нужно предпочесть передавать оба вместе или оба отдельно/последовательно? –

ответ

3

Если вы читаете the UDP(7) manual page вы увидите эту запись FIONBIO:

... Возвращает размер следующего ожидающего дейтаграммы в целом числе в байтах, или 0, если нет дейтаграммы находится на рассмотрении. ...

(выделено мной.)

Так FIONBIO не дает вам количество байтов в буфере, но размер следующего пакета. Если новый пакет не получен, вы получите нуль.

+0

Боюсь, что «просто выполнение другого вызова будет давать вам остальную часть данных» - неверно. UDP не работает таким образом. – Damon

+0

@ Дамон. Ты прав, я не использую UDP достаточно часто. :) –

1

В буфере хранится датаграмма, а не только данные полезной нагрузки.

3

Что ваш код делает это:

  • Запрос размера следующей дейтаграммы в приемном буфере, который равен 52 байт
  • прочитанные 40 байт из следующей дейтаграммы в буфере прием и отбрасывать остальные
  • Проверьте recvfrom возвращается 0, , который никогда не будет случай, так как сокет не установлен неблокирующая - мой плохой, проглядели MSG_DONTWAIT флаг
  • Предположим, что прием нулевой длины означает, что «другой конец закрыл соединение» - это для TCP. UDP не имеет соединений или «трубок», нет ничего, что можно было бы закрыть: нулевая длина означает дейтаграмму «без полезной нагрузки», которая состоит только из заголовка (это совершенно законно!)
  • Запросить размер следующей дейтаграммы в приеме буфер (который равен нулю, поскольку его нет)

Примечательно, что UDP работает на основе строгой базы данных. Вы не можете прочитать половину дейтаграммы (ну, вы можете ... но ... читать дальше).

Независимо от того, что произойдет, вы всегда получите полную дейтаграмму или ничего. Дейтаграммы могут быть потеряны (и будет потеряно), но не байты. Вы также должны прочитать полную дейтаграмму («читать», как в «потреблять») также.

Чтение дейтаграммы удаляет его из буфера отправки. Чтение половины дейтаграммы (или любой ее части) удаляет ее из буфера отправки, , отбрасывая остальные.

+0

Не могли бы вы взглянуть на мой вопрос? http://stackoverflow.com/questions/41192809/udp-restreaming-using-socket-resulting-in-null-packets –