Робота з мережею

Для роботи з протоколами FTP та HTTP у бібліотеці Qt є класи QFtp та QHttp. Вони досить зручні для організації обміну файлами через мережу.

Класи QFtp та QHttp засновані на низькорівневому класі QSocket, який реалізує уявлення сокетів TCP. Протокол TCP працює у термінах потоків даних, що передаються між вузлами мережі. Клас QSocket, у свою чергу, реалізований поверх QSocketDevice - тонкої "обертки" навколо платформо-залежного мережевого API операційної системи. Клас QSocketDevice підтримує протоколи TCP та UDP.

У цьому розділі ми говоритимемо про ці чотири, і деякі інші класи, і покажемо - як з ними працювати. Розкажемо, як організувати обмін файлами через мережу. Протокол TCP буде використовуватися нами при написанні додатків-серверів та відповідних додатків-клієнтів. Аналогічно, протокол UDP буде використовуватися для написання передавальної та приймаючої частини додатків. Розуміння принципів роботи класів QFtp і QHttp зазвичай ні в кого не викликає труднощів, навіть у новачків, проте для розуміння принципів роботи з класами QSocket і QSocketDevice бажано мати деякий досвід роботи з мережами.

13.1. Клас QFtp.

Клас QFtp призначений для створення клієнтських програм, що працюють з протоколом FTP. Він реалізує набір функцій для виконання найбільш поширених операцій цього протоколу, включаючи get(), put(), remove() і mkdir().

Усі операції виконуються асинхронно. Коли викликається функція, така як get() або put(), керування відразу ж повертається програмі, а власне передача даних починає здійснюватися, коли керування знову переходить у цикл обробки подій Qt. Завдяки цьому під час виконання FTP-команд не виникає ефекту "заморожування"інтерфейс з користувача.

Демонстрацію можливостей QFtp почнемо з показу, як завантажити файл із сервера, використовуючи функцію get(). Припустимо, що основний клас програми MainWindow має завантажити прейскурант із FTP-сайту.

Клас визначає публічну функцію getPriceList(), яка відповідає за отримання файлу з прейскурантом, та приватний слот ftpDone(bool), який викликається після закінчення прийому файлу. Так само в класі визначено дві змінні: ftp, відповідальну за взаємодію з FTP-сервером, та файл, що використовується для запису файлу на диск. У конструкторі виконується з'єднання між сигналом done(bool), екземпляра класу QFtp і слотом ftpDone(bool). Об'єкти класу QFtp видають цей сигнал після обробки всіх запитів. Параметр bool свідчить про наявність можливих помилок. Функція getPriceList() завантажує файл ftp://ftp.trolltech.com/topsecret/csv/price-list.csv та зберігає його під ім'ям price-list.csv у поточному каталозі.

Починається функція зі спроби відкрити файл у поточному каталозі. Потім виконується послідовність із п'яти FTP-команд. Другий аргумент функції get() задає пристрій, в який буде здійснюватися запис даних, що приймаються.

Команди FTP ставляться у чергу і виконуються у циклі обробки подій Qt. Після завершення обробки всіх команд об'єкт QFtp видає сигнал done(bool), який підключений до слота ftpDone(bool).

Після виконання FTP-команд файл закривається. Якщо виникла помилка, перед користувачем виводиться відповідне повідомлення.

Клас QFtp надає такі операції: connectToHost(), login(), close(), list(), cd(), get(), put(), remove(), mkdir(), rmdir() і rename(). Всі ці функції ставлять відповідні команди у чергу та повертають ідентифікаційнийномер команди. Будь-які команди FTP можуть бути використані за допомогою функції rawCommand(). Наприклад, так виглядає виконання команди SITE CHMOD:

Об'єкти QFtp перед виконанням команди видають сигнал commandStarted(int), а по завершенні - commandFinished(int, bool). Аргумент int – це ідентифікаційний номер команди. Якщо вас цікавить хід виконання окремих команд, вам доведеться зберігати їх ідентифікаційні номери, при викликі відповідної функції. Завдяки цьому з'явиться можливість надати користувачеві детальнішу інформацію про хід процесу. Наприклад: Інший спосіб забезпечення користувача зворотним зв'язком із процесом - використовувати сигнал stateChanged().

Проте, у більшості програм нас цікавить лише результат виконання всієї послідовності команд. У цьому випадку ми просто з'єднуємося з сигналом done (bool), який видається після виконання останньої команди в послідовності.

Тепер розглянемо складніший приклад:

Примірник класу Downloader спробує завантажити всі файли з каталогу FTP. Ім'я каталогу визначається як QUrl, при виклику конструктора. Клас QUrl - це стандартний клас з бібліотеки Qt, який реалізує інтерфейс для роботи з рядками URL і виділення їх окремих частин, таких як ім'я файлу, шлях до файлу, протокол і порт. У конструкторі перш за все виконується перевірка рядка URL - вона повинна починатися з комбінації символів: "ftp:". Потім з URL витягується номер порту, якщо порт не вказано, передбачається використання стандартного FTP-порту - 21.

Потім виконуються з'єднання сигнал-слот і в чергу поміщаються 4 FTP-команди. Остання запитує у сервера список файлів і видає сигнал listInfo(const QUrlInfo &), коли від сервера приходить чергове ім'я файлу.Цей сигнал пов'язаний із слотом listInfo(), що відповідає за скачування файлу.

Аргумент типу QUrlInfo надає детальну інформацію про файл. Якщо це звичайний файл (не каталог) і доступний для читання, то робиться спроба завантажити його, викликом get(). Об'єкт QFile використовується для збереження локальної копії файлу, він створюється оператором new, а вказівник на нього зберігається в динамічному масиві (векторі) OpenFiles. Слот ftpDone() викликається після завершення виконання послідовності команд або у разі виникнення помилки. Функція видаляє всі об'єкти QFile, закриваючи всі файли. (Файли автоматично закриваються деструктором класу QFile.)

Якщо помилок не виникло, то порядок виконання команд та видачі сигналів буде таким:

Якщо помилка виникла, наприклад, під час скачування п'ятого файлу з двадцяти наявних, то файли, що залишилися, не будуть скачуватися. Якщо вас це не влаштовує, то можна спробувати виконувати завантаження файлів по одному - запускати команду GET, чекати на появу сигналу done (bool) і тільки після цього запускати GET для чергового файлу. А функції listInfo() -- просто створювати список імен файлів у каталозі сервера. У цьому випадку порядок виконання команд та видачі сигналів буде наступним: Інший варіант вирішення проблеми полягає у використанні окремого об'єкта QFtp для кожного файлу. Це дозволить виконувати паралельне завантаження кількох файлів через різні FTP-з'єднання. Реалізацією функції main() ми завершуємо розгляд програми. Якщо користувач вказує URL у командному рядку, файли завантажуються з вказаного каталогу, в іншому випадку - з каталогу ftp://ftp.example.com/.

В обох вищенаведених прикладах файл скачується за допомогою функції get() і записується на дискза допомогою об'єкта QFile. Якщо ж прийнятий файл потрібно зберегти в пам'яті, то для цього чудово підійде клас QBuffer, похідний від класу QIODevice - обгортки навколо класу QByteArray. Наприклад:

Якщо необхідно відображати хід виконання завантаження файлу, можна зв'язати сигнал dataTransferProgress(int, int), класу QFtp, зі слотом setProgress(int, int) класу QProgressBar або QProgressDialog. Крім того, можна прив'язати сигнал canceled(), класу QProgressBar або QProgressDialog зі слотом abort(), класу QFtp.