LinuxForum.Fans
Тема

tcp-соединения мешают друг другу

Автор: Peter 19 сообщений 3146 просмотров
#288436

Здравствуйте!

Возникла проблема с одновременной работой tcp-соединений - если их больше
одного, они очень мешают друг другу. Есть программа-сервер, она создаёт от
одного до четырёх потоков, каждый из них принимает видеопоток от web-камеры
по tcp и выводит его на экран. От камер идёт mjpg - каждый кадр отдельным jpg,
размер 50-100 kB. Есть программа-клиент на другом компьюетере, в ней тоже
создаются потоки для получения jpg от программы-сервера и вывода на экран.
Соединение происходит по порту, номер которого определяется номером камеры.
После соединения сервер пересылает jpg клиенту.

10 кадров в секунду, сервер 4-ядерный, клиент 2-ядерный, загрузка невелика.
Программа на Qt.

Если поток один, то всё нормально - например, сервер передал 1000 jpg,
а клиент принял все или на один-два меньше, . Отладочный вывод после каждого jpg
показывает, что сколько послано, столько и принято, отображается тоже нормально.
Если же потоков два, то возникают огромные задержки на клиенте - в десятки
секунд. Приняться и обработаться успевают не все посланные jpg - например, оба
серверных потока послали 2800, а из клиентских один принял 2530, другой 1550 и
при этом огромная задержка в выводе, т.к. отображение замедленное. Всегда в
одном потоке отображение идёт со скоростью, близкой к нормальной, а тормозит
другой. Отладочный вывод показывает, что после приёма очередного jpg принято
оказалось больше байтов. В чём может быть причина?

Это порисходит на ALTLinux 6.0 сертифицированной. В Ubuntu этого явления
не было, но там была другая проблема - система не распределяла загрузку
по ядрам, всё ложилось на 1-е ядро, а в ALTLinux загрузка ядер равномерная.

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

Пётр.

#288508

батенька, сдаётся у вас где-то гонка, вы что-то не так распаралели...

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

#288518

1. А предыдущую проблему решили (где байты непонятным образом менялись) или костыль соорудили?
2. Полагаю вы знаете, что с GUI работает только один поток и вы не возложили на него какой либо ф-ии типа декодирования?
3. Без кода можно придумать 100500 причин и ни одной правильной.

#288520

1. Применил костыль. Я заметил, что после правильно принятой команды из 8 байтов можно принять ещё 8,
которые я так и не понял, откуда берутся, поэтому я их просто принимаю и игнорирую. Кажется, я об этом
говорил.
2. Потоки для приёма и декодирования jpg не имеют GUI, они запускаются из программного модуля, тоже
не имеющего GUI, в нём только происходит отрисовка декодированных jpg. В сервере создаёется поток,
в нём создаётся объект для приёма jpg от камеры по одному tcp-соединению и с этим потоком связывается
аналогичный из клиента по другому tcp-соединению.
3. Не могу поручится, что нет гонок, но потоки создаются одинаково, а распределением ресурсов
занимается система. Каждый клиентский поток связывается с нужным ему серверным по своему порту.
Во всяком случае, в Ubuntu была программа, написанная не мной, которая принимала потоки от одной камеры,
а потом имитировала 4, точно также отдавая поток jpg потоку в клиентской программе по tcp по отдельному
порту и никаких задержек не было. Эта же схема используется и сейчас.

Пётр.

#288531

"Та же схема" не показатель.... Очень важна реализация. А реализацию видите только вы.

Остается только пожелать банальное "попробуйте локализовать проблему":
- отправляйте изображение не с камер, а просто одну и ту же картинку
- отключите "отрисовку".
- считайте полученные байты и пакеты - анализируйте

Вы формируете некий пакет или просто байты картиники в чистом виде пересылаете?
Может считывая первую картинку заодно считываете кусок следующей и грохаете его за ненадобностью.
.......
И так далее N-цать разных причин....

#288570

Да и реализация та же самая - клиент соединяется с сервером через QTcpSocket и получает от него jpg.
Как только от камеры получен jpg, он целиком пересылается клиенту, пересылается именно столько байтов,
сколько занимает jpg. Также есть подозрение, что диск, с которого ставились системы, был не вполне
в порядке, т.к. ещё на одном компьютере с него установка не пошла. Попробуем переустановить с другого.

Ещё странное явление, вчера несколько раз было на клиенте, а сегодня и на сервере - интерфейс
замерзает на много секунд. На клиенте после этого ничего не происходило, а сервер делался
неработоспособным, в частности, при попытке ввести команду выдавалось соощение, что файловая
система в read-only.

Пётр.

#288603
"Peter" написал:
Да и реализация та же самая - клиент соединяется с сервером через QTcpSocket и получает от него jpg.

Т.е. вы взяли и скопировали код ни чего не меняя?

"Peter" написал:
Как только от камеры получен jpg, он целиком пересылается клиенту, пересылается именно столько байтов,
сколько занимает jpg.

При обмене данными вы исходите из предположения, что если вы на сервере отправляете два раза (по одному соединению), например:
1 отправка: 0A 0F 13
2 отправка: 1B 3C 17

ТО клиент считывает так же обязательно в два этапа ровно по три байта?

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

#288617

Можно сказать, что скопировал: на сервере в каждом потоке запускается QTcpServer, он слушает определённый
порт, на клиенте в потоке QTcpSocket подключается к этому порту и сервер начинает пересылать ему jpg.

Пересылается одной командой write весь jpg с указанием размера. На стороне клиента сигнал readyRead
вырабатывается много раз, на каждый вызывается readAll, когда примется весь jpg, он декодируется и
отрисовывается. Приём организован так, что данные не теряются.
Что уходит, то и приходит, это видно на экране.

Пётр.

#288620

"Можно сказать" - не считается. Может вы где то что то пропустили и не учли.

Т.е. в ситуации когда при переданном как описал выше получается:
1 прием 0A 0F 13 1B
2 прием 3С 17
обрабатывается нормально? Т.е. получаются две картиники.


Кстати, а почему TCP, а не UDP? Вам важно, чтоб каждый кадр не потерялся? Может ну его.... Только предусмотреть ситуацию когда очередность кадров нарушится.

#288643

Да, там важно, чтобы кадры не терялись и отображались правильно.
Там нечего пропускать, QTcpSocket на клиенте соединяется с QTcpServer на сервере,
сервер фиксирует факт соединения установкой флага и начинает передавать jpg.

Я не понял, что имеется в виду под 1 и 2. tcp-протокол гарантирует сохранение очерёдности пакетов,
вот они и принимаются. Когда придёт пакет, содержащий конец jpg, он извлекается, обрабатывается
и приём продолжается с начала следующего jpg независимо от того, пришло оно отдельно или
вместе с концом предидущего.

Пётр.

#288679

Возможно, там ещё проблемы с отведением памяти. Поток сам по себе ничего не делает,
только в методе run по команде start запускается на исполнение экземпляр класса Camera
для обработки, а после выхода из run он уничтожается. У моего предшественника он
и создавался в run и при выходе удалялся и он и сам поток. Я обнаружил, что в клиенте это
часто приводит к зависанию клиентской программы при попытке дать ей другой видеопоток
или при закрытии и даже к зависанию системы. Тогда я по возможности не стал удалять
экземпляр потока, а объект типа Camera стал создавать отдельной командой до старта
потока. Тогда клиент стал работать устойчиво, тормозных явлений в вышеописанном виде
тоже не было.

Это в Ubuntu, но когда я стал запускать сервер в ALTLinux, а клиент остался в Ubuntu,
в нём снова стали происходить сбои. Сейчас обе программы в ALTLinux.

Сегодня я для эксперимента попробовал вернуться к варианту, когда объект Camera
создаётся в методе run. Запустил клиент, когда сервер был готов отдавать ему два потока.
Тормозных явлений почти не было, на клиенте обе картинки отображались синхронно, было
только отставание на 1-2 секунды от картинки на сервере. А когда я на сервере добавил
клиенту 3-й поток, опять пошли задержки на десятки секунд, причём чем больше номер потока
на клиенте, тем больше задержка. А при отключении потоков зависла и клиентская программа
и система, в которой он была запущен, не реагировала даже на АAlt+SysRq+b.

Пётр.

#288801
"Peter" написал:
Сегодня я для эксперимента попробовал вернуться к варианту, когда объект Camera
создаётся в методе run. Запустил клиент, когда сервер был готов отдавать ему два потока.
Тормозных явлений почти не было, на клиенте обе картинки отображались синхронно, было
только отставание на 1-2 секунды от картинки на сервере. А когда я на сервере добавил
клиенту 3-й поток, опять пошли задержки на десятки секунд, причём чем больше номер потока
на клиенте, тем больше задержка.

Как у вас кадр полученный с камеры передается в поток отправляющий его клиенту?

#288919

В серверной программе jpg извлекается из потока и одной командой write посылается клиенту
с указанием длины, как я уже говорил.
Сегодня человек, хорошо знакомый с ALTLinux посмотрел на это и сказал, что это баг в драйвере
сетевой карты, то же самое насчёт того, что звук перестал записываться из программы - баг в ядре.
У него были похожие проблемы со звуком при переходе на другое ядро.
Такие задержки при отображении секунды и десятки секунд - значат, как он сказал, что всё это
свопится на диск, но почему всё делается так долго, непонятно. Надо обращаться в поддержку
ALTLinux, т.к. дистрибутив сертифицированный и лицензию мы у них покупаем.

Пётр.

#288925
"Peter" написал:
В серверной программе jpg извлекается из потока и одной командой write посылается клиенту
с указанием длины, как я уже говорил.

не не не.
Я не о том. Правильно ли я понимаю: есть отдельный поток программы получающий данные с отдельной камеры, есть несколько потоков обслуживающих подключенных клиентов, которые связанны именно с этой конкретной камерой. Т.е. картинк поступает с камеры в программу в один поток, а уходит в другом/других. Мой вопрос был как именно осуществляется передача картинки из потока поучающего изображение с камеры в поток работающий с клиентом?

#288946
voral написал:
[quote="Peter"]В серверной программе jpg извлекается из потока и одной командой write посылается клиенту
с указанием длины, как я уже говорил.

не не не.
Я не о том. Правильно ли я понимаю: есть отдельный поток программы получающий данные с отдельной камеры, есть несколько потоков обслуживающих подключенных клиентов, которые связанны именно с этой конкретной камерой. Т.е. картинк поступает с камеры в программу в один поток, а уходит в другом/других. Мой вопрос был как именно осуществляется передача картинки из потока поучающего изображение с камеры в поток работающий с клиентом?[/quote]
Есть отдельные потоки в серверной программе, каждый из них получает видео от одной камеры,
каждый от своей. Их может быть до четырёх.
В каждом из них создаётся один объект типа QTcpServer, слушающий порт, номер которого
однозначно определяется тем, от какой камеры этот поток получает видео.
Есть отдeльные потоки в клиентской программе, их тоже может быть до четырёх.
В каждом из них создаётся один объект типа QTcpSocket. Он соединяется с тем QTcpserver,
поток с которым получает видео от камеры, желаемой клиентом.

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

Сам поток ничего не делает, только создаёт объект, в котором и происходите вся обработка.
Этот объект называется Camera, это осталось от моего предшественника. В одном варианте
он создаётся в методе run потока, т.е. при его старте и при остановке уничтожается.
В другом он создаётся до старта потока отдельной функцией. Оказалось, что при 1-м
варианте клиентская программа работает неустойчиво, при 2-м нормально.

Пётр.

#289774

Попробовал с помощью tcpdump посмотреть, что приходит на клиентскую сторону.
На ней двух-ядерный Intel 1.66GHz и Intel 945GM.

Если сервер посылает три потока jpg, в каждом 5раз/с, то программа успевает отобразить все три.
Если частота 10к/с, то при одном потоке успевает его вывести, но есть задержка
по отношению к этому же изображению в серверной программе на 1-2 секунды.
В клиентской программе каждый jpg декодируется немного больше, чем 50ms, 55-57.
jpg посылаются вместе со служебной информацией, это 12 байтов, в которых есть номер
jpg с момента, когда сервер начал их передавать.

Если посылать два потока, то, например, сервер в каждом послал 2371 jpg,, в сохранённом
выводе tcpdump 2202, программа успела обработать 2201. Примерно то же для другого
потока. При этом задержка в выводе изображения в клиентской программе по сравнению
с ним же в серверной уже 10-20 секунд. Если один поток отключить, то задержка в другом
постепенно выбирается.

Пётр.

#289839

а размер картинки какой?

#289894
voral написал:
а размер картинки какой?

С камер идёт 1280x1024, а выводится вдвое сжатая по обоим измерениям,
чтобы в этом же размере уместилось 4 картинки. Вообще похоже, что не хватает мощности
клиентского модуля, т.к., когда сегодня я запустил клиентскую программу на четырёх-ядерном
модуле с Intel i7 2.00GHz и тем же Intel 945GM, на котором до этого запускалась серверная,
в ней без всяких задержек отображались 4 видеопотока при 10к/с.

Пётр.

#290104

Сегодня скомпилировал клиентскую программу с явным указанием в pro-файле thrread, mmx, sse и sse2.
Сравнил командой cmp её и предидущую, скомпилированную без явного указания mmx и sse,
они совпали в точности, но работать она стала вроде бы быстрее. Теперь на клиентском
модуле она уверенно успевает воспроизводить два потока с частотой 10к/с, а до этого
обычно не упевала. Qt по умолёанию многопоточная, pthread включается в любом случае.

На серверной программе я раньше не наблюдал заметного прироста производительности
при явном включении mmx и sse, прирост был единицы процентов, да и сейчас клиентская
программа на четырёх-ядерном модуле Intel 4x2.00GHZ отображала 4 потока при 10к/с
без явного влючения mmx и sse.

Пётр.

Войдите или зарегистрируйтесь, чтобы ответить.