Russian Qt Forum
Ноябрь 23, 2024, 10:22 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Проектирование приложения  (Прочитано 3662 раз)
PazDim
Гость
« : Май 25, 2019, 17:51 »

Добрый день. Нужна помощь - не понимаю архитектуры приложений для QT. В книжке написано, что библиотека использует слоты и сигналы, мол это лучше, чем callback. Но что делать с программами, которые не предполагают использования сигналов? Сейчас стоит задача в работе по TCP - необходим автомат, который принимает данные, обрабатывает и отправляет ответ (просто бесконечный цикл, опрашивающий сокет и отправляющий в него данные). У QTcpSocket чтение данных выполняется через сигналы. Попробовал вынести его в отдельный поток (основной занят автоматом и выполняения event loop не предполагает) - сигнал Connected генерируется, ReadyRead вроде тоже, но не могу нормально писать - write приходится вызывать из основного потока, ругается. Плюнул, попробовал следующий код:

Код:
#include <QTcpSocket>
#include <QByteArray>
#include <QThread>
#include <QHostAddress>
#include <QDebug>
int main()
{
    QTcpSocket socket;
    socket.bind(1234);
    /* Выполняем подключение */
    socket.connectToHost(QHostAddress("192.168.0.5"), 1234);
    while (socket.state() != QTcpSocket::SocketState::ConnectedState)
        qDebug() << socket.state();
    qDebug() << "Connected";

    QByteArray buf;

    while (true)
    {
        buf.clear();
        buf.append(static_cast<char>(0x02));
        qDebug() << socket.write(buf);
        socket.flush();

        QThread::sleep(1);
    }
    return 0;
}

Сокет постоянно находится в состоянии попытки соединения, то есть ему даже для соединения нужен Event Loop? Как уже писал, основной цикл не предполагает передачи управления QT, лепить в каждую итерацию обработку сигналов тоже не хочется. Помню, что когда-то получилось работать по сети без сигналов, но выглядело и работало очень криво. Если идея бесконечного цила неверная, то как правильно проектировать программу? Обычное консольное приложение, не думал что так голова будет болеть.
Записан
PazDim
Гость
« Ответ #1 : Май 25, 2019, 18:40 »

Заменить бесконечный цикл таймером в одну миллисекунду и лепить все в одном потоке - это правильно?
Записан
PazDim
Гость
« Ответ #2 : Май 26, 2019, 11:01 »

Блин, в таймере получается здоровенный автомат с кучей кейсов. Логика вроде "отправить запрос, подождать ответа, обработать, отправить следующий, подождать" на сигналы-слоты совсем не ложится. Пока вижу решение как добавление отдельного потока, который не будет иметь цикла событий и будет вызывать слоты других классов с помощью QMetaObject::invokeMethod. Это нормальный подход?
Записан
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #3 : Май 26, 2019, 12:23 »

Если ваша программа только принимает данные и отправляет ответ, то вообще никакого цикла и таймера не нужно. И уж, конечно, не нужны потоки.
Создаете сокет, соединяете его сигнал readyRead со своим слотом, в котором обрабатываются полученные данные. В нем реализуете логику определения того, что нужные данные получены (по размеру или признакам начала/конца). Скорее всего, понадобится  буфер, куда записываете полученные данные.
В этом слоте реализуете запись в сокет ответа после того, как все данные получите. Не забудьте эти данные из буфера удалить.
Потом сокет подключаете (connectToHost) к порту и все.
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
PazDim
Гость
« Ответ #4 : Май 26, 2019, 13:24 »

Если ваша программа только принимает данные и отправляет ответ, то вообще никакого цикла и таймера не нужно. И уж, конечно, не нужны потоки.
Создаете сокет, соединяете его сигнал readyRead со своим слотом, в котором обрабатываются полученные данные. В нем реализуете логику определения того, что нужные данные получены (по размеру или признакам начала/конца). Скорее всего, понадобится  буфер, куда записываете полученные данные.
В этом слоте реализуете запись в сокет ответа после того, как все данные получите. Не забудьте эти данные из буфера удалить.
Потом сокет подключаете (connectToHost) к порту и все.
Не все алгоритмы ложатся на такую структуру.
Отправить команду А.
Подождать, пока не придет ответ Б.
Отправить команду В.
Подождать, пока не придет ответ Г.
Разделять их на разные слоты слишком сложно - иногда приходится ждать дополнительного условия. Нужно накручивать таймеры для таймаутов.
Записан
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 872


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #5 : Май 26, 2019, 14:43 »

Из постановки следовало только то, что программ должна отвечать на поступающие данные.
Если требуются запросы с ожиданием ответа, то можно после запроса организовать ожидание в цикле обработки событий.  Этот цикл должен завершаться по по вашему сигналу, испускаемому из слота после получения данных, ну и по заданному таймауту.
А запросы, как вы и писали, можно делать по таймеру.
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #6 : Май 26, 2019, 16:58 »

Посмотрите на waitForConnected и waitForReadyRead
Записан
PazDim
Гость
« Ответ #7 : Май 26, 2019, 17:16 »

Буду привыкать к сигналам-слотам. Посмотрим, что из этого получится.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #8 : Май 26, 2019, 20:23 »

Сигнал-слоты всегда можно завернуть в синхронку с помощью QFuture/QFutureInterface. Я так недавно делал с QNetworkAccessManager'ом - мне надо было сделать синхронную функцию "скачай файл и положи сюда". Сделал тред с эвентлупом и завернул в футуру метод: QFuture<bool> download(QUrl src, QString dst).
Записан
PazDim
Гость
« Ответ #9 : Май 26, 2019, 20:29 »

Спасибо, почитаю, что за звери. Пока остановился на таймерах и автоматах.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.221 секунд. Запросов: 22.