Здравствуйте!
Есть программа на Qt и в ней объект типа QUdpSocket.
Оказалось, что, если по сигналу readyRead() принимать от него дейтаграммы
не по одной, а как рекомендуется - все, сколько их накопилось, то минут через
6-8 начинают катастрофически теряться пакеты - десятками и работоспособность
восстанавливается только после того, как источнику запретить и снова разрешить
передавать пакеты. До этого момента потерь пакетов нет.
Если же по сигналу readyRead() принимать только один пакет и тут же его обрабатывать,
то тоже случаются потери пакетов, изредка по многу сразу подряд, но работоспособность
не утрачивается. Там видеокамера передаёт jpg по UDP-протоколу и в этом случае просто
портится какой-то один jpg. Дейтаграммы по 1024 байта.
В программе буфер для пакетов достаточно большой - на 250 пакетов, но попробую
увеличить. До сбоя обычно оказывается сразу готово 1-3 пакета, иногда 11-13, изредка
до 73, а сам jpg до 170kB. wireshark показал, что пакеты реально теряются. Это может
происходить, если программа в какой-то действительно не успевает их все принять.
А вопрос в том, где в системе буфер для QUdpSocket? В справке сказано, что в Qt
не заводится буфер для QUdpSocket и он полагается на буферизацию, осуществляемую
системой. Это буфер программный или это буфер аппаратный в контроллере
сетевой карты? Если программный, то регулируется ли он и чем и как?
Для QTcpSocket, наоборот, можно задать размер буфера, но по умолчанию он неограничен.
Пётр.
буфер для QUdpSocket
Это буфер в ядре. Поменять его размер для открытого сокета можно через setsockopt, уровень SOL_SOCKET, опция SO_RCVBUF
Только он по умолчанию немаленький:
[console]$ cat /proc/sys/net/core/rmem_default
212992[/console]
так что ваша программа действительно не успевает разгребать поток.
Это буфер в ядре. Поменять его размер для открытого сокета можно через setsockopt, уровень SOL_SOCKET, опция SO_RCVBUF
Только он по умолчанию немаленький:
[console]$ cat /proc/sys/net/core/rmem_default
212992[/console]
так что ваша программа действительно не успевает разгребать поток.
Благодарю за информацию, я на уровне Linux сокетами почти не занимался.
Однако буфер действительно большой. jpg идут с частотой не более 16Гц,
размер jpg должен быть меньше 200kB, а если он всё-таки больше размера
этого буфера, то как действует система? Начинает заполнять этот буфер
с начала, как кольцевой или останавливает приём пакетов из сетевой карты?
Сетевая карта, как мне сказали, не даёт согласия на приём следующих пакетов,
если её аппаратный буфер заполнен.
Наверное, программный буфер перезаписывается с начала, поскольку
wireshark показывает потерю пакетов. Однако я пробовал выделять приём
пакетов по readyRead() в отдельный поток, который делал только это и
уведомлял программу, что все пакеты приняты, но явление осталось.
Пётр.
Ничего не останавливается. Все принятые фреймы разбираются и распределяются по назнвачениям. Переполняется буфер уже вашего сокета, и проблемы вашей программы на работу сети влиять не должны.
Буфер чистится, но не знаю насколько. Это же udp, здесь доставка пакетов не гарантируется, и можно безнаказанно дропать принятые пакеты, так что стратегия может быть любая. Я бы вычистил сразу старую половину, но что там на самом деле - не знаю, и закладываться нельзя ни на какую стратегию.
В вашем случае вероятно этот кусок писать на низком уровне: socket(), bind(), цикл по recvfrom() - в отдельном треде либо через добавления события к select() из qt mailn loop. qt чересчур умный и поэтому не слишком быстрый
Ещё раз благодарю за разъяснения. Я тоже думал, не придётся ли делать это на уровне системы,
но это, конечно, нежелательно, тем более, что работать правильно должно было уже давно.
Однако процессор четырёхядерный, 2.0ГГц, был только один этот видеопоток и ядра были
загружены пренебрежимо мало. Сначала постараюсь побыстрее освобождать системный буфер.
Пётр.
Похоже, что причина потери пакетов всё же не в системном буфере,
а в реализации связей в Qt между программными единицами.
Я пробовал буфер и уменьшать и увеличивать, всё равно пакеты теряются.
Сейчас подпрограмма извлечения jpg из принятых пакетов вызывается
после приёма последнего пакета, но делается это пока нерационально -
в цикле по количеству пакетов, завтра сделаю, чтобы она обрабатывала
их за один вызов. Потом по start() запускаается run() из потока, в котором
jpg декодируется, а он после декодирования эмитирует сигнал, соединённый
со слотом в программном модуле с обработкой пакетов и там для декодирующего
потока вызываются quit() и wait().
Здесь и есть самое узкое место, ещё раньше обнаружили, что, если декодирование
не вызывать, то пакеты не теряются. У друого человека в программе для Windows
поток, собирающий или декодирующий jpg, не помню точно, сам один раз в миллисекунду
проверяет, готовы ли для него данные и там всё работает нормально. Наверное, попробую
вместо start() создавать событие.
А сейчас, при последнем запуске минут на 13-23, у нескольких jpg потерялось
по одному пакету между 1-м и последним.
Пётр.
Я сделал вариант с использованием системного сокета, но в результате оказалось,
что это камера не посылает пакеты с периодом 32768, т.е. один пакет теряется.
Автор программы для камеры признал ошибку.
Пётр.
Что же это за камера такая? В них во всех давным-давно linux. Axis ещё в до-arm-овские времена собственные soc-чипы выпускал. Мы их даже одно время использовали в своих проектах.
Там камера с программой, написанной немного в другой фирме. Мы с ними общаемся, вот и вчера человек от них приходил.
Была техническая ошибка, то ли в анализе пременной, то ли в её определении.
Пётр.