Виталий Антипов
Jan. 19, 2021, 1:51 a.m.

Qt: INTERNAL ERROR: failed to install GetMessage hook: 1158

server

Здравствуйте. Вопрос по простенькому серверу на ПК. Устройства-клиенты подключаются к локальной сети, передают данные на сервер. Задача сервера при каждом подключении клиента (раз в минуту) создавать отдельный поток, в нем принимать данные и отправлять в интерфейс. После получения данных подключение и поток должны закрываться. При тестировании через пару дней приложение упало со следующей ошибкой: "Qt: INTERNAL ERROR: failed to install GetMessage hook: 1158, Текущий процесс использовал все системные разрешения по управлению объектами диспетчера окон". Логи показывают, что соединения/потоки закрываются не сразу после приема данных, а через десятки часов. Похоже что слишком много потоков создано одновременно. Собственно и вопрос к гуру, почему не закрываются потоки сразу, как это можно реализовать? (впервые работаю с сервером и потоками).

main.cpp
  1. #include "mainwindow.h"
  2. #include "myserver.h"
  3. #include <QApplication>
  4. #include <QDir>
  5.  
  6. int main(int argc, char *argv[])
  7. {
  8. QApplication a(argc, argv);
  9. MyServer server;
  10. server.startServer();
  11. MainWindow w;
  12. QObject::connect(&server, SIGNAL(signal1(qreal)), &w, SLOT(onAppendData(qreal)));
  13. w.show();
  14. return a.exec();
  15. }
mainwindow.h
  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QtCharts>
  6. #include <QChartView>
  7. #include <QChart>
  8. #include <QDateTime>
  9. #include <QDebug>
  10.  
  11. using namespace QtCharts;
  12.  
  13. class MainWindow : public QMainWindow
  14. {
  15. Q_OBJECT
  16.  
  17. public:
  18. MainWindow(QWidget *parent = nullptr);
  19. ~MainWindow();
  20.  
  21. QLineSeries *series = new QLineSeries();
  22. QDateTimeAxis *axisX = new QDateTimeAxis;
  23. QValueAxis *axisY = new QValueAxis();
  24. int i = 0;
  25. QDateTime minX;
  26. QDateTime maxX;
  27. qreal minY;
  28. qreal maxY;
  29.  
  30. public slots:
  31. void onAppendData(qreal);
  32. };
  33. #endif // MAINWINDOW_H
mainwindow.cpp
  1. #include "mainwindow.h"
  2.  
  3. MainWindow::MainWindow(QWidget *parent)
  4. : QMainWindow(parent)
  5. {
  6. QWidget *widget = new QWidget;
  7. setCentralWidget(widget);
  8. QVBoxLayout *layout = new QVBoxLayout;
  9. layout->setMargin(5);
  10. widget->setLayout(layout);
  11. QChartView *chartView = new QChartView(this);
  12. layout->addWidget(chartView);
  13.  
  14. QChart *chart = new QChart();
  15. chart->addSeries(series);
  16. chart->setTitle("График температуры во времени");
  17.  
  18. axisX->setFormat("hh:mm:ss");
  19. axisX->setTitleText("Время");
  20. axisX->setTickCount(10);
  21. chart->addAxis(axisX, Qt::AlignBottom);
  22. series->attachAxis(axisX);
  23.  
  24. axisY->setTitleText("Температура, °С");
  25. axisY->setLabelFormat("%g");
  26. axisY->setTickCount(10);
  27. chart->addAxis(axisY, Qt::AlignLeft);
  28. series->attachAxis(axisY);
  29. chartView->setChart(chart);
  30. }
  31.  
  32. MainWindow::~MainWindow()
  33. {
  34. }
  35.  
  36. void MainWindow::onAppendData(qreal data)
  37. {
  38. QDateTime x = QDateTime::currentDateTime();
  39. qreal y = data;
  40. if(i == 0){
  41. minX = x;
  42. maxX = x;
  43. minY = y;
  44. maxY = y;
  45. axisX->setRange(minX, maxX);
  46. axisY->setRange(minY, maxY);
  47. series->append(x.toMSecsSinceEpoch(), y);
  48. } else {
  49. minX = axisX->min();
  50. maxX = axisX->max();
  51. minY = axisY->min();
  52. maxY = axisY->max();
  53. axisX->setRange(x < minX ? x : minX, x > maxX ? x : maxX);
  54. axisY->setRange(y < minY ? y : minY, y > maxY ? y : maxY);
  55. if(i>99999){
  56. series->remove(0);
  57. minX = QDateTime::fromMSecsSinceEpoch(series->at(0).x());
  58. axisX->setRange(minX, x > maxX ? x : maxX);
  59. }
  60. series->append(x.toMSecsSinceEpoch(), y);
  61. }
  62. i++;
  63. }
myserver.h
  1. #ifndef MYSERVER_H
  2. #define MYSERVER_H
  3.  
  4. #include <QTcpServer>
  5. #include "mythread.h"
  6.  
  7. class MyServer : public QTcpServer
  8. {
  9. Q_OBJECT
  10. public:
  11. explicit MyServer(QObject *parent = 0);
  12. void startServer();
  13. signals:
  14. void signal1(qreal);
  15.  
  16.  
  17. public slots:
  18. void onSignalData(qreal);
  19. protected:
  20. void incomingConnection(qintptr socketDescriptor);
  21.  
  22. };
  23.  
  24. #endif // MYSERVER_H
  25.  
myserver.cpp
  1. #include "myserver.h"
  2.  
  3. MyServer::MyServer(QObject *parent) :
  4. QTcpServer(parent)
  5. {
  6. }
  7.  
  8. void MyServer::startServer()
  9. {
  10. int port = 5555;
  11. if(!this->listen(QHostAddress("192.168.1.201"),port))
  12. {
  13. qDebug() << "Could not start server";
  14. }
  15. else
  16. {
  17. qDebug() << "Listening to port " << port << "...";
  18. }
  19.  
  20. }
  21.  
  22. void MyServer::incomingConnection(qintptr socketDescriptor)
  23. {
  24. qDebug() << socketDescriptor << " Connecting...";
  25. MyThread *thread = new MyThread(socketDescriptor, this);
  26. connect(thread, SIGNAL(signalData(qreal)), this, SLOT(onSignalData(qreal)), Qt::DirectConnection);
  27. connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
  28. thread->start();
  29. }
  30.  
  31. void MyServer::onSignalData(qreal data){
  32. qDebug()<<"слот сработал"<<data;
  33. emit signal1(data);
  34. }
mythread.h
  1. #ifndef MYTHREAD_H
  2. #define MYTHREAD_H
  3.  
  4. #include <QThread>
  5. #include <QTcpSocket>
  6. #include <QDebug>
  7.  
  8. class MyThread : public QThread
  9. {
  10. Q_OBJECT
  11. public:
  12. explicit MyThread(qintptr ID, QObject *parent = 0);
  13.  
  14. void run();
  15.  
  16. signals:
  17. void error(QTcpSocket::SocketError socketerror);
  18. void signalData(qreal);
  19.  
  20. public slots:
  21. void readyRead();
  22. void disconnected();
  23.  
  24. private:
  25. QTcpSocket *socket;
  26. qintptr socketDescriptor;
  27. };
  28.  
  29. #endif // MYTHREAD_H
mythread.cpp
  1. #include "mythread.h"
  2.  
  3. MyThread::MyThread(qintptr ID, QObject *parent) :
  4. QThread(parent)
  5. {
  6. this->socketDescriptor = ID;
  7. }
  8.  
  9. void MyThread::run()
  10. {
  11. qDebug() << " Thread started";
  12. socket = new QTcpSocket();
  13. if(!socket->setSocketDescriptor(this->socketDescriptor))
  14. {
  15. emit error(socket->error());
  16. return;
  17. }
  18. connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
  19. connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
  20. qDebug() << socketDescriptor << " Client connected";
  21. exec();
  22. }
  23.  
  24. void MyThread::readyRead()
  25. {
  26. QByteArray Data = socket->readAll();
  27. qDebug() << socketDescriptor << " Data in: " << Data;
  28. emit signalData(Data.toFloat());
  29. }
  30.  
  31. void MyThread::disconnected()
  32. {
  33. qDebug() << socketDescriptor << " Disconnected";
  34. socket->deleteLater();
  35. exit(0);
  36. }
3

Do you like it? Share on social networks!

7
Алексей Внуков
  • Jan. 19, 2021, 10:16 p.m.

добрый. давайте начнем с того что у вас выпонено в реализации которую сам Qt даже не рекомендует на данный момент.
1. новый синтаксис сигналов и слотов
2. переопределять run() и наследовать от QThread - не верно, делаем рабочий класс. делаем ему мувтосред и потом старт потока
3. создание сервера и его обвязка - можно вынести в класс интерфейса, и я бы серверу выделил отдельный поток от интерфейса, и потом уже в потоке работа с сокетами
теперь о deleteLater() - оманда выполнется только тогда когда в данном сокете ничего уже происходить не будет, возможно есть какое-то обращение к данному сокету, из-за чего он долго не удаляется. если вам не нужен постоянный конект к серверу, со стороны клиента делайте конект->отправка данных->подтверждение о получении данных->дисконект

    Алексей, спасибо за ответ.
    1. Этот синтаксис сигнал/слот вроде старый и во всех туториалах, про Qt не рекомендует где-то пропустил. Каким образом тогда общаться между потоками?
    2. Спасибо за совет, буду пробовать.
    3. Клиентом является esp8266, которая отдав данные уходит в глубокий сон разрывая соединение. В сокете ничего не происходит, но почему-то deleteLater() не срабатывает сразу, в чем и загвоздка.
    Буду думать/пробовать, спасибо еще раз.

      Qt не рекомендует - это в большей степени про работу потоков. По поводу обмена данными между потоками ничего не изменилось, все так же через сигнал слот. Про новый синтаксис можно почитать тут

        Алексей Внуков
        • Jan. 20, 2021, 6:49 p.m.
        • (edited)
        • The answer was marked as a solution.

        по поводу п.3. Ситуация может быть в том что esp8266 не корректно закрывает соединение. Это проблема всех соединений TCP, если от устройства не пришел корректный сигнал дисконекта то сервер будет держать поток до последнего. только где-то там может произойти событие дисконекта после чего произойдет разрыв и сработает deleteLater(). В таком случае могу посоветовать через какое-то время отправить любой запрос (можно даже пустой) на клиет, и, поскольку клиент со своей стороны уже отрубил соединение и пошел спать, серверу сразу прийдет событие что нет соединения, произойдет дисконект и выполнится deleteLater().

          Огромное спасибо! Про некорректное закрытие соединения со стороны клиента даже не подумал, скорее всего и вправду в этом проблема, надо разбираться с esp8266. За ссылку отдельное спасибо.

            Алексей Внуков
            • Jan. 20, 2021, 6:57 p.m.

            и еще одно, поскольку, по факту, каждое TCP-соединение это отдельный поток, подумайте о целесообразности дополнительно делать новые потоки

              ESP8266 и вправду не корректно закрывало соединение. Причина оказалась банальной: перед функцией ухода в глубокий сон ESP.deepSleep(60e6) необходимо было поставить небольшую задержку delay(1). Ваше предположение оказалось верным.

                Comments

                Only authorized users can post comments.
                Please, Log in or Sign up
                • Last comments
                • AK
                  April 1, 2025, 11:41 a.m.
                  Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
                • Evgenii Legotckoi
                  March 9, 2025, 9:02 p.m.
                  К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
                • VP
                  March 9, 2025, 4:14 p.m.
                  Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
                • ИМ
                  Nov. 22, 2024, 9:51 p.m.
                  Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                • Evgenii Legotckoi
                  Oct. 31, 2024, 11:37 p.m.
                  Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup