![]() |
Здравствуйте, гость ( Вход | Регистрация )
![]() |
Гость_me_* |
![]()
Сообщение
#1
|
Гости ![]() |
Доброго времени суток..
Не так давно начал разбираться с взаимодействием потоков в qt и осознал что оч мощно запутался. Есть задача: написать многопотоный ftp клиент который парсит файлик с заданиями и закачивает с разных ftp ресурсов файлы. Многопоточный просто для того чтобы самому в этом хорошенько разобраться. Решил организовать это следующим образом на каждое задание создаётся поток и далее вся работа через QFtp. Насколько я понял QFtp асинхронный, а так как мне требуется помимо непосредственно работы с ftp в этом же потоке обрабатывать некоторые действия со скачанными файлами то требуется вынести это в соотвествующие слоты. Не понятно мне стало вот что: - насколько я понимаю слоты прикреплённые к соотвествующим сигналам из Qftp НЕ будет выполнятся до тех пор пока не будет вызван exec в потоке? Но тогда я так понима.ю мне придётся как минимум обращаться к потоку из главного потока дважды. Первый раз на старт потока а второй на запуск задания (просто слот с перечнем комманд на коннект и на скачку) - получается что всю работу с ftp надо будет полностью выносить в отдельные слоты, что видится мне несколько грамоздким. Вообщем требуется квалифицированная помощь))) з.ы. Заранее хочу сказать, что листал книги по qt, читал документацию, но разобрался не во всём и эти моменты для меня туманны, так что просьба к великому гуглу не отсылать))) з.з.ы Какой-нибудь простенький пример будет оч кстати.. ток главный смысл в том что программа будет автоматизированна и без участие человека и без гуи... так что явный инициатор QFtp c точки зрения запуска заданий отсуствует.. Заранее спасибо! |
|
|
![]() |
ViGOur |
![]()
Сообщение
#2
|
![]() Мастер ![]() ![]() ![]() ![]() ![]() ![]() Группа: Модератор Сообщений: 3296 Регистрация: 9.10.2007 Из: Москва Пользователь №: 4 Спасибо сказали: 231 раз(а) Репутация: ![]() ![]() ![]() |
Работу с FTP не нужно выносить в отдельные слоты.
Давай попробуем разделить изучение того, что тебе нужно на шаги: 1. Для лучшего понимания почитай Сигналы и слоты () (если еще не читал) 2. Глянь пример работы с ftp: FTP Example. А за одно и класс QFtp, для получения файла нужно использовать: int QFtp::get ( const QString & file, QIODevice * dev = 0, TransferType type = Binary ). По данному примеру и сделай свою реализацию, пускай и консольную, GUI в примере чисто для визуализации. 3. Прочитай Поддержка потоков в Qt. А теперь, если ты на отлично понял 1, 2, 3 пункты, попробуй 'нарисовать' более ясную картину твоей реализации. Потому как у тебя явно полная каша в голове. ![]() |
|
|
Гость_me_* |
![]()
Сообщение
#3
|
Гости ![]() |
А теперь, если ты на отлично понял 1, 2, 3 пункты, попробуй 'нарисовать' более ясную картину твоей реализации. Потому как у тебя явно полная каша в голове. ![]() Вы не поверите.. про потоки и слоты я читал.. и пример тоже смотрел. Причём после предложения прочитать ещё раз я честно перечитал это ещё раз дабы освежить в памяти. В голове у меня действительно сейчас небольшая каша..((( причём гланым образом из-за того что я не понимаю как конкретно работает асинхронный QFtp. Ведь как я понял функции аля connect и get сразу же возвращают значение при этом не блокируют поток... Вообще самое печальное что я не так давно делал и клиентское приложение и серверное приложение где было по потоку на соединение.. но там вроде всё получилось но видать не доразобрался до конца. Попробую объяснить что я хотел бы сделать: Главный поток парсит файл. На каждое задание он создаёт поток и передаёт конструктору потока адрес сервера, логин пароль, путь к папке на сервере, путь к локальной папке. В потоке происходит подключение к ftp серверу. Считывается листинг директории (с вхождением в глубь) и проверяется по маске нужные нам файлы. После этого файлы скачиваются. Неясный мне момент вот в чём: void MyThread::run() { QFtp ftp = new QFtp; ftp->connectToHost("ftp.trolltech.com"); ftp->login(); ftp->list(); ...... ftp->close(); exec(); } 1) list будет испускать сигнал о каждой записи. Предположим что мы сделали коннект этого сигнала со слотом класса MyThread который их как-то обрабатывает. Скажите сигнал listInfo будет обрабатываться после exec или до??? или exec вообще здесь никак не влияет? Если бы речь шла о функциях которые отрабатывают сразу всё было бы ясно.. но здесь ведь асинхронная работа.. Или exec нам в данном случае вообще не нужен? 2) получается что если там где стоит .... я бы захотел как-то обрабатывать файлы которые скачал (будем считать что после list идёт скачка файлов) то тогда мне надо выносить это в слот, потому что скачивание файлов тоже асинхронно и мы можем перейти к выполнению этих операций тогда когда файлы ещё по существую не скачались. 3) добавлю к вопросу 1. Когда мы будем извещаться о том отработала например функция connectToHost (cоотвествующий сигнал что функция done)? в произвольный момент времени после её вызова или строго после exec? з.ы. exec как много в этом звуке=(((( |
|
|
SABROG |
![]()
Сообщение
#4
|
![]() Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 1207 Регистрация: 8.12.2008 Из: Russia, Moscow Пользователь №: 446 Спасибо сказали: 229 раз(а) Репутация: ![]() ![]() ![]() |
Скажите сигнал listInfo будет обрабатываться после exec или до??? Во время, скорее всего. Я так понял класс хоть и ассинхронный, но он не поточный, а событийный (event loop). Если ты застрянешь в каком-нибудь методе надолго и возвращение не будет в eventloop, то и слоты твои вызываться не будут. Сообщение отредактировал SABROG - 18.2.2009, 0:46 |
|
|
Гость_me_* |
![]()
Сообщение
#5
|
Гости ![]() |
Скажите сигнал listInfo будет обрабатываться после exec или до??? Во время, скорее всего. Я так понял класс хоть и ассинхронный, но он не поточный, а событийный (event loop). Если ты застрянешь в каком-нибудь методе надолго и возвращение не будет в eventloop, то и слоты твои вызываться не будут. Эмм... не понял. Что такое поточный класс? "Если ты застрянешь в каком-нибудь методе надолго и возвращение не будет в eventloop, то и слоты твои вызываться не будут." - тоже не понял что вы имели ввиду( |
|
|
SABROG |
![]()
Сообщение
#6
|
![]() Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 1207 Регистрация: 8.12.2008 Из: Russia, Moscow Пользователь №: 446 Спасибо сказали: 229 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Гость_me_* |
![]()
Сообщение
#7
|
Гости ![]() |
|
|
|
SABROG |
![]()
Сообщение
#8
|
![]() Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 1207 Регистрация: 8.12.2008 Из: Russia, Moscow Пользователь №: 446 Спасибо сказали: 229 раз(а) Репутация: ![]() ![]() ![]() |
Это из-за того, что QAbstrackSocket не блокирующий. Не знаю как в linux'е, а в виндах асинхронность добивается путем принятия WM_сообщений окном от ОС. Естественно, если в этот момент выполняется какой-нибудь метод, то сама программа не получит сообщение о том, что пришли какие-то данные, пока не произойдет возврат в основной event loop, где Qt сможет забрать сообщения от ОС с помощью API.
Вообще, если сравнивать гуишный и консольный вариант программ Qt, то консольные программы тоже бывают разные. Тот вариант консольного приложения, что делает Qt не будет работать в DOS'e например. Это обычное Win32 console приложение, где главным окном является окно консоли и оно естественно поддерживает windows messages. В *nix'ах это уже другое приложение, оно будет работать без X-Server'а, там механизм событий уже иной. Сообщение отредактировал SABROG - 18.2.2009, 10:19 |
|
|
Гость_me_* |
![]()
Сообщение
#9
|
Гости ![]() |
Это из-за того, что QAbstrackSocket не блокирующий. Не знаю как в linux'е, а в виндах асинхронность добивается путем принятия WM_сообщений окном от ОС. Естественно, если в этот момент выполняется какой-нибудь метод, то сама программа не получит сообщение о том, что пришли какие-то данные, пока не произойдет возврат в основной event loop, где Qt сможет забрать сообщения от ОС с помощью API. Вроде всё стало понятно, спасибо! Эх.. а с блокирующими сокетами было бы всё несколько проще.. |
|
|
Константин |
![]()
Сообщение
#10
|
![]() Студент ![]() Группа: Участник Сообщений: 69 Регистрация: 9.2.2009 Пользователь №: 539 Спасибо сказали: 15 раз(а) Репутация: ![]() ![]() ![]() |
Гость_me_*,
для испускания сигналов петля событий не нужна. т.е. вообще. петля событий потоку необходима, если в этом потоке требуется асинхронно выполнять слоты. твоя задача решается довольно просто, если разобраться со взаимодействием потоков в кутэ. /* хотел привести ссылку на почитать, но не нашёл ( */ |
|
|
SABROG |
![]()
Сообщение
#11
|
![]() Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 1207 Регистрация: 8.12.2008 Из: Russia, Moscow Пользователь №: 446 Спасибо сказали: 229 раз(а) Репутация: ![]() ![]() ![]() |
Эх.. а с блокирующими сокетами было бы всё несколько проще.. Для этого были созданы такие методы: bool waitForConnected ( int msecs = 30000 ) bool waitForDisconnected ( int msecs = 30000 ) virtual bool waitForReadyRead ( int msecs = 30000 ) Т.е. все блокируется на 30 секунд, а там уже можешь либо повторить попытку, либо пользователю выдать еrror. |
|
|
Гость_me_* |
![]()
Сообщение
#12
|
Гости ![]() |
Гость_me_*, петля событий потоку необходима, если в этом потоке требуется асинхронно выполнять слоты. Так у меня и получается что на асинхронный сигнал QFtp надо вызывать слот в этом же потоке.. (например как я уже говорил для анализа и работы листинга директории) |
|
|
SABROG |
![]()
Сообщение
#13
|
![]() Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 1207 Регистрация: 8.12.2008 Из: Russia, Moscow Пользователь №: 446 Спасибо сказали: 229 раз(а) Репутация: ![]() ![]() ![]() |
Так у меня и получается что на асинхронный сигнал QFtp надо вызывать слот в этом же потоке.. (например как я уже говорил для анализа и работы листинга директории) Соединяй нужные сигналы от QFtp со слотами QThread и выполняй exec() в run(). Все, что будет одти до exec - выполняется один раз при запуске потока, потом exec() входит в "бесконечный" цикл, т.е. возврата из run() не будет до тех пор, пока не будет выход из eventloop, а это произойдет уже при завершении потока. Таким образом все слоты будут обрабатываться не до и не после exec, а во время. QFtp::list() и прочие команды возвращают свой уникальный ID команды. Можно использовать этот номер, чтобы отслеживать, когда команда завершится. До завершения команды у тебя будут приходить сигналы listInfo(), можно его обрабатывать и заполнять свой список нужным образом, отсортировывая не нужное. В конце придет сигнал commandFinished(int id), IDшник сравниваешь с IDшником возвращенным от list()'a и если это он, то начинаем выкачивать файлы по одному из своего списка. Удаляя выкаченные файлы из списка. На завершение выкаченных файлов тоже будут приходить сигналы со своим id, там ты и будешь удалять из своего списка то, что скачалось. Когда список станет нулевым можно закрыть соединение QFtp::close(), удалить экземпляр QFtp::deleateLater() и завершить поток, т.е. сделать тот самый выход из exec() - quit()/exit(0). Сообщение отредактировал SABROG - 18.2.2009, 16:14 |
|
|
Константин |
![]()
Сообщение
#14
|
![]() Студент ![]() Группа: Участник Сообщений: 69 Регистрация: 9.2.2009 Пользователь №: 539 Спасибо сказали: 15 раз(а) Репутация: ![]() ![]() ![]() |
по всей видимости, тут ещё имеют место проблемы с пониманием асинхронное работы сокетов кутэ.
Цитата void MyThread::run() { QFtp ftp = new QFtp; ftp->connectToHost("ftp.trolltech.com"); ftp->login(); ftp->list(); ...... ftp->close(); exec(); } так делать не нужно. тем более, что ftp будет создан в родительском потоке. вызывай exec(), а всю работу с фтп вынеси из run() |
|
|
Гость_me_* |
![]()
Сообщение
#15
|
Гости ![]() |
Таким образом все слоты будут обрабатываться не до и не после exec, а во время. Тоесть начиная с того как был вызван непосредственно exec и мы вошли в event loop и до тех пока не вызовем завершить поток, т.е. сделать тот самый выход из exec() - quit()/exit(0) Большое спасибо!!! По этому поводу все вопросы в сущности и были) |
|
|
Гость_me_* |
![]()
Сообщение
#16
|
Гости ![]() |
по всей видимости, тут ещё имеют место проблемы с пониманием асинхронное работы сокетов кутэ. Так оно и есть... тем более, что ftp будет создан в родительском потоке Я там торопился... не указатель а просто QFtp ftp; Тоесть сделать так:
а дальше уже ловить успешное подключение и действовать дальше ? |
|
|
Гость_me_* |
![]()
Сообщение
#17
|
Гости ![]() |
не забыв конечнео же сделать connectы всё какие нужно...
|
|
|
Гость_me_* |
![]()
Сообщение
#18
|
Гости ![]() |
Цитата Классы QHttp и QFtp предоставляют поддержку протоколов HTTP и FTP на клиентской стороне. Так как эти два протокола используются для решении похожих задач, классы QHttp и QFtp имеют много общих особенностей: * Неблокирующее поведение. QHttp и QFtp асинхронны. Вы можете отправить ряд команд (также называемые "запросы" для HTTP). Команды выполнятся позже, когда управление вернется к циклу событий Qt. * ID команд. Каждая команда имеет свой уникальный номер ID, который используется для слежения за выполнением команд. Например, QFtp передает сигналы commandStarted() и commandFinished() с ID команды для каждой выполняемой команды. У QHttp тоже имеются подобные сигналы requestStarted() и requestFinished(). * Индикаторы процесса передачи данных. QHttp и QFtp посылают сигналы при передаче данных (QFtp::dataTransferProgress(), QHttp::dataReadProgress() и QHttp::dataSendProgress()). Вы можете соединить данные сигналы, например, с QProgressBar::setProgress() или QProgressDialog::setProgress(). * Поддержка QIODevice. Оба класса поддерживают загрузку в и скачивание из QIODevice, дополнительно к API, основанному на QByteArray. Есть два основных способа использования QHttp и QFtp. Самый обычный способ состоит в том, чтобы отслеживать ID команд и следить за выполнением каждой команды, соединившись с соответствующим сигналом. Другой способ состоит в том, чтобы запланировать все команды сразу и соединиться только с сигналом done(), который посылается, когда все команды были выполнены. Первый способ требует большего количества работы, но дает больший контроль над выполнением конкретных задач и позволяет вводить дополнительные команды, основанные на результате предыдущих. Что позволяет обеспечить пользователю детализированную обратную связь. Чёрт, как-то забыл совсем что есть общая статья про сетевое взаиможействие и совсем упустил из виду что тамCC написанно. |
|
|
Константин |
![]()
Сообщение
#19
|
![]() Студент ![]() Группа: Участник Сообщений: 69 Регистрация: 9.2.2009 Пользователь №: 539 Спасибо сказали: 15 раз(а) Репутация: ![]() ![]() ![]() |
а дальше уже ловить успешное подключение и действовать дальше ? не забыв конечнео же сделать connectы всё какие нужно... именно. в таком случае при невозможности соединиться (connectToHost) можно выйти и завершить поток, даже не запуская его петлю событий (т.к. вряд ли тебе нужен поток с неподключённым фтп-клиентом) Сообщение отредактировал Константин - 18.2.2009, 16:49 |
|
|
Гость_me_* |
![]()
Сообщение
#20
|
Гости ![]() |
Всем спасибо! Тему я думаю на этом закрыть можно..
|
|
|
Константин |
![]()
Сообщение
#21
|
![]() Студент ![]() Группа: Участник Сообщений: 69 Регистрация: 9.2.2009 Пользователь №: 539 Спасибо сказали: 15 раз(а) Репутация: ![]() ![]() ![]() |
|
|
|
Гость_me_* |
![]()
Сообщение
#22
|
Гости ![]() |
В продолжение этой темы.. как-то всё не доходили руки сесть за всё это, но как наконец-то сел начались новые непонимания..
Вначале код: FtpThread.h:
FtpThread.cpp
main.cpp
Вывод в консоль=(((
Толи на оба QFtp даётся общая последовательность ID команд.. толи не знаю. Если это так то очевидно лучше использовать currentCommand () + слоты на запуск команды и конец команды...? Что я делаю не так?? |
|
|
SABROG |
![]()
Сообщение
#23
|
![]() Профессионал ![]() ![]() ![]() ![]() ![]() Группа: Участник Сообщений: 1207 Регистрация: 8.12.2008 Из: Russia, Moscow Пользователь №: 446 Спасибо сказали: 229 раз(а) Репутация: ![]() ![]() ![]() |
Рекомендация по поводу использования NULL: http://www.prog.org.ru/topic_7458_0.html
|
|
|
Гость_me_* |
![]()
Сообщение
#24
|
Гости ![]() |
Забыл вырезать из вывода QFtp(0x80564b0) - эт я решил в какой=то момент посмотреть на адреса объектов QFtp для каждого потока...
|
|
|
Гость_me_* |
![]()
Сообщение
#25
|
Гости ![]() |
Последовательность ID действительно одна для всех объектов QFtp ((
Прийдётся через currentThreadId и дальнейший анализ что за команда выполняется.. меня вот кстати всегда мучал вопрос а не может ли случиться так, что если мы ловим сигнал от commandStarted и в слоте вызываем currentThreadId не может ли она хотя бы теоретически вернуть другую команду... |
|
|
![]() ![]() ![]() |
![]() |
|
Текстовая версия | Сейчас: 27.6.2025, 3:43 |