LINUX.ORG.RU

Получение и обработка данных используя QTcpSocket

 


0

1

Господа, добрый день. Возникла одна проблема, которую не получается решить в силу отсутствия соответствующий познаний о Qt.

Есть примерно следующий код:

class DataProcessing : public QObject
{
Q_OBJECT
public:
    void data(); //Получаем и сохраняем данные
    void processing() {/*Обработка полученных данных*/}
private:
    RemoteDate ds; //Класс для работы с сервером посредством QTcpSocket
    char* buffer; //Буфер для данных сервера
}

void DataProcessing::data()
{
    //Какой-то код

    ds.getData(buffer); //Получаем данные от удаленного сервера

    //Снова какой-то код
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    DataProcessing dp;
 
    dp->data();
    dp->processing();
 
    return a.exec();
}

Суть в том, что необходимо получить данные от удаленного сервера. Класс RemoteData производит всю процедуру обмена сообщениями и получения нужных данных от сервера в функции getData(char*), используя для этого класс QTcpSocket. После того, как данные получены, их необходимо обработать функцией processing().

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

На StackOverflow находил возможное решение данный проблемы. В кратце решение выглядело так:

void DataProcessing::data()
{
    //Какой-то код
    QEventLoop qel;
    QObject::connect(&dp, SIGNAL(proceed()), &qel, SLOT(quit()));

    ds.getData(buffer); //Получаем данные от удаленного сервера

    qel.exec(); 
    
    //Снова какой-то код
}
Собственно решение работает, но я сильно сомневаюсь в его корректности и подозреваю, что есть более элегантные решения. И стоит добавить, что функций получения данных от сервера может быть намного больше, в зависимости от того, что именно требуется получить. В связи с этим у меня два вопроса:

1) Как правильно отложить функцию обработки данных до момента пока эти самые данные не будут получены полностью?

2) Создавая QEventLoop мы блокируем обработку других события только для объекта, где QEventLoop был создан? Или инкапсулируя этот объект в другой (Yobj), унаследованный от QObject, мы блокируем обработку событий и этого Yobj?

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

PS. Класс DataProcessing будет включаться в другие классы. Здесь в main() он расположен сугубо для демонстрации.



Последнее исправление: mkam (всего исправлений: 1)

Нужно создать сигнал для момента, когда данные будут готовы, и в момент готовности делать emit; также надо сделать слот, который обрабатывает готовые данные. В конце обработки выпускать сигнал, который будет соединён с QApplication::quit.

QEventLoop не нужен, QApplication::exec() сам по себе будет выполняться, пока не будет вызван слот quit. Тут пример минимального консольного приложения, без асинхронности, но для добавления асинхронности достаточно добавить сигналы/слоты и ловить сигнал от QTcpSocket, чтобы затем выпустить уже свой сигнал, назвав его, к примеру, dataReady().

P.S. Добавлю, что в Qt сигналы и слоты — это потокобезопасная реализация паттерна Observer. Сигнал означает событие, с помощью метода QObject::connect можно соединить событие с обработчиком — «слотом». При этом на одном событии может быть сколько угодно слотов. При уничтожении одного из объектов или явном вызове QObject::disconnect связь сигнала и слота разрывается.

О многопоточности: каждый слот будет вызываться в том потоке, где находится объект, в классе которого объявлен слот; объект по дефолту находится в том потоке, где был вызван конструктор класса (но можно переместить объект в рантайме). Если объект сигнала и объект со слотом находятся в одном потоке, то вызов будет мгновенный и строчка «emit mySignal()» не вернёт управление, пока слот не выполнится; иначе будет асинхронный вызов с состоянием гонки.

quiet_readonly ★★★★
()
Последнее исправление: quiet_readonly (всего исправлений: 3)
Ответ на: комментарий от quiet_readonly

Спасибо за ответ. Но видимо я пример привёл плохой. DataProcessing не будет непосредственно в main() располагаться. Он будет включен в другой класс, этот другой класс ещё куда-то и т.д.

Вообще пишу имплементацию SQL-драйвера в разрезе Qt. На удаленном компе стоит sqlite. Необходимо написать драйвер, который будет прозрачно работать с базой на удаленном компьютере.

mkam
() автор топика
Ответ на: комментарий от mkam

Спасибо за ответ. Но видимо я пример привёл плохой. DataProcessing не будет непосредственно в main() располагаться

Это ничего не меняет, сигналы и слоты как раз и созданы для взаимодействия разных объектов.

quiet_readonly ★★★★
()
Ответ на: комментарий от quiet_readonly

Нужно создать сигнал для момента, когда данные будут готовы, и в момент готовности делать emit; также надо сделать слот, который обрабатывает готовые данные. В конце обработки выпускать сигнал, который будет соединён с QApplication::quit.

В таком случае, мне не совсем понятны данные строки. Зачем выпускать сигнал на выход из приложение после обработки? И я указывал, что обработку данных должен инициировать пользователь.

mkam
() автор топика
Ответ на: комментарий от mkam

В таком случае, мне не совсем понятны данные строки. Зачем выпускать сигнал на выход из приложение после обработки? И я указывал, что обработку данных должен инициировать пользователь.

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

Вот, кстати, накидал простое приложение, которое забирает содержимое по url, печатает и завершает работу.

quiet_readonly ★★★★
()
Ответ на: комментарий от quiet_readonly

Вот, кстати, накидал простое приложение, которое забирает содержимое по url, печатает и завершает работу.

Опять же. Обработка данных вынесена в слот и запускается по сигналу после получения данных. Это противоречит моей фраз:

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

Почему возникло такое ограничение.
В классе QSqlQuery есть следующие две функции QSqlQuery::exec(QString&) и QSqlQuery::value(int) - отвечающие за исполнение (и получение результата) запроса и выдачу результатов непосредственно. Эти функции вызывают пользователем явно. Если провести аналогию с приведённым тобой кодом, функцию printUrl требуется явно вызывать в main.cpp. Ограничение диктуется интерфейсом, который должен предоставлять драйвер.

mkam
() автор топика

Открыл Шлее. Он в 40 главе, для блокировки основного потока пока не будут получены данные тоже пользует QEventLoop. Но всё равно как-то костыльно выглядит.

mkam
() автор топика

Упоротых тред. Лор уже не торт и всё такое.

connect(socket, readyRead(), this, incomming());
...
QByteArray packet;
...
incomming()
{
  packet += socket->read();
  if(уже_приехали(packet)) {
    do_packet(packet);
    packet.clear();
  }
}

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

Хорошо, чувак. Я тебе как самому неупоротому в этом треде ещё раз напишу то, что писал в первом сообщении.

инициировать обработку данных должен пользователь класса.

mkam
() автор топика
Ответ на: комментарий от nanoolinux

И чё?

И то что вышеприведённый код нахер не нужен.

Слабо сделать очередь или что?

Ты вообще пост читал первый? Видимо слабо, если данный тред существует. По делу ссылки на соответствующие раздели документации или что-то менее абстрактное «слабо сделать очередь» будут?

mkam
() автор топика
Ответ на: комментарий от mkam

По делу ссылки на соответствующие раздели документации или что-то менее абстрактное «слабо сделать очередь» будут?

Какие тебе разделы документации? do_packet из моего примера суёт пакеты в очередь. Тот, который

инициировать обработку данных должен пользователь класса.

извлекает из очереди и обрабатывает.

Вот тебе пример очереди, если сам не в силах кнопочки понажимать. Мутекс с семафором можешь из pthreads взять.

#pragma once

#include <list>
#include "Mutex.hpp"
#include "Sem.hpp"

template <typename T>
class Queue
{
  std::list <T> queue_;
  Mutex mutex_;
  Sem sem_;
  public:
    void post(const T &t) {
      mutex_.lock();
      queue.push_back(t);
      mutex_.unlock();
      sem_.add();
    }
    
    T wait() {
      sem_.wait();
      mutex_.lock();
      T ret = queue_.front();
      queue_.pop_front();
      mutex_.unlock();
      return ret;
    }
};

nanoolinux ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.