Linux - ioctl с FIONREAD всегда 0 - PullRequest
       24

Linux - ioctl с FIONREAD всегда 0

11 голосов
/ 08 августа 2011

Я пытаюсь узнать, сколько байтов доступно для чтения на моем сокете TCP. Я называю ioctl с флагом «FIONREAD», который должен дать мне это значение. Когда я вызываю функцию, я получаю в качестве возвращаемого значения 0 (то есть без ошибки), но мой целочисленный аргумент получает значение 0. Это не будет проблемой, но когда я вызываю метод recv (), я на самом деле считываю некоторые байты из сокета. Что я делаю не так?

// здесь какой-то код:

char recBuffer[BUFFERLENGTH] = {0};
int bytesAv = 0;
int bytesRead = 0;
int flags = 0;
if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )
{
    // Error
}
if ( bytesAv < 1 )
{
    // No Data Available
}
bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);

Когда я вызываю функцию recv, я внимательно читаю некоторые действительные данные (которые я ожидал)

Ответы [ 4 ]

11 голосов
/ 08 августа 2011

Это происходит очень быстро, поэтому вы ничего не видите. Что вы делаете:

  • ioctl: Есть ли данные для меня? Нет, пока ничего
  • recv: блокировать, пока не будет данных для меня. Некоторое (короткое) время спустя: Вот ваши данные

Так что, если вы действительно хотите увидеть FIONREAD, просто подождите.

/* Try FIONREAD until we get *something* or ioctl fails. */
while (!bytesAv && ioctl (m_Socket,FIONREAD,&bytesAv) >= 0)
    sleep(1);
9 голосов
/ 14 августа 2013

Реальный ответ здесь - использовать select (2), как сказал cnicutar.Тоби, ты не понимаешь, что у тебя есть состояние гонки.Сначала вы смотрите на сокет и спрашиваете, сколько там байтов.Затем, пока ваш код обрабатывает блок «здесь нет данных», аппаратные средства и операционная система получают асинхронные байты для вашего приложения.Итак, к тому времени, когда вызывается функция recv (), ответ «нет доступных байтов» больше не верен ...

if ( ioctl (m_Socket,FIONREAD,&bytesAv) < 0 )
{ // Error 
}

// BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!

if ( bytesAv < 1 ) // AND HERE!
{
    // No Data Available
    // BUT BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!
}

// AND MORE BYTES MIGHT BE RECEIVED BY HARDWARE/OS HERE!

bytesRead = recv(m_Socket,recBuffer,BUFFERLENGTH,flags);
// AND NOW bytesRead IS NOT EQUAL TO 0!

Конечно, небольшой сон, вероятно, исправил вашу программу два годаназад, но это также научило вас ужасной практике кодирования, и вы упустили возможность научиться правильно использовать сокеты с помощью select ().

Далее, как сказал Кароли Хорват, вы можете сказать recv не читатьбольше байтов, чем вы можете сохранить в буфере, который передал пользователь. Тогда интерфейс вашей функции станет «Этот fn вернет столько байтов, сколько доступно в сокете, но не больше, чем [размер буфера, который вы передали]».

Это означает, что этой функции больше не нужно беспокоиться об очистке буфера.Вызывающая сторона может вызывать вашу функцию столько раз, сколько необходимо для очистки всех ее байтов (или вы можете предоставить отдельный fn, который отбрасывает данные оптом и не связывает эту функцию в какой-либо конкретной функции сбора данных).Ваша функция более гибкая, не делая слишком много вещей.Затем вы можете создать функцию-обертку, которая отвечает вашим потребностям в передаче данных конкретного приложения и которая вызывает fn get_data fn и clear_socket fn, необходимые для этого конкретного приложения.Теперь вы создаете библиотеку, которую можете переносить из проекта в проект, и, возможно, работать на работу, если вам так повезло, что у вас есть работодатель, который позволяет вам брать с собой код.

3 голосов
/ 09 февраля 2013

Используйте select (), затем ioctl (FIONREAD), затем recv ()

2 голосов
/ 08 августа 2011

Вы не делаете ничего плохого, если вы используете блокировку ввода / вывода, recv () будет блокировать, пока данные не станут доступны.

...