Виталий Антипов
Виталий Антипов18 січня 2021 р. 14:51

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

server

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

main.cpp
#include "mainwindow.h"
#include "myserver.h"
#include <QApplication>
#include <QDir>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyServer server;
    server.startServer();
    MainWindow w;
    QObject::connect(&server, SIGNAL(signal1(qreal)), &w, SLOT(onAppendData(qreal)));
    w.show();
    return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtCharts>
#include <QChartView>
#include <QChart>
#include <QDateTime>
#include <QDebug>

using namespace QtCharts;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QLineSeries *series = new QLineSeries();
    QDateTimeAxis *axisX = new QDateTimeAxis;
    QValueAxis *axisY = new QValueAxis();
    int i = 0;
    QDateTime minX;
    QDateTime maxX;
    qreal minY;
    qreal maxY;

public slots:
    void onAppendData(qreal);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QWidget *widget = new QWidget;
    setCentralWidget(widget);
    QVBoxLayout *layout = new QVBoxLayout;
    layout->setMargin(5);
    widget->setLayout(layout);
    QChartView *chartView = new QChartView(this);
    layout->addWidget(chartView);

    QChart *chart = new QChart();
    chart->addSeries(series);
    chart->setTitle("График температуры во времени");

    axisX->setFormat("hh:mm:ss");
    axisX->setTitleText("Время");
    axisX->setTickCount(10);
    chart->addAxis(axisX, Qt::AlignBottom);
    series->attachAxis(axisX);

    axisY->setTitleText("Температура, °С");
    axisY->setLabelFormat("%g");
    axisY->setTickCount(10);
    chart->addAxis(axisY, Qt::AlignLeft);
    series->attachAxis(axisY);
    chartView->setChart(chart);
}

MainWindow::~MainWindow()
{
}

void MainWindow::onAppendData(qreal data)
{
    QDateTime x = QDateTime::currentDateTime();
    qreal y = data;
    if(i == 0){
        minX = x;
        maxX = x;
        minY = y;
        maxY = y;
        axisX->setRange(minX, maxX);
        axisY->setRange(minY, maxY);
        series->append(x.toMSecsSinceEpoch(), y);
    } else {
    minX = axisX->min();
    maxX = axisX->max();
    minY = axisY->min();
    maxY = axisY->max();
    axisX->setRange(x < minX ? x : minX, x > maxX ? x : maxX);
    axisY->setRange(y < minY ? y : minY, y > maxY ? y : maxY);
    if(i>99999){
        series->remove(0);
        minX = QDateTime::fromMSecsSinceEpoch(series->at(0).x());
        axisX->setRange(minX, x > maxX ? x : maxX);
    }
    series->append(x.toMSecsSinceEpoch(), y);
    }
    i++;
}
myserver.h
#ifndef MYSERVER_H
#define MYSERVER_H

#include <QTcpServer>
#include "mythread.h"

class MyServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit MyServer(QObject *parent = 0);
    void startServer();
signals:
    void signal1(qreal);


public slots:
void onSignalData(qreal);
protected:
    void incomingConnection(qintptr socketDescriptor);

};

#endif // MYSERVER_H

myserver.cpp
#include "myserver.h"

MyServer::MyServer(QObject *parent) :
    QTcpServer(parent)
{
}

void MyServer::startServer()
{
    int port = 5555;
    if(!this->listen(QHostAddress("192.168.1.201"),port))
    {
        qDebug() << "Could not start server";
    }
    else
    {
        qDebug() << "Listening to port " << port << "...";
    }

}

void MyServer::incomingConnection(qintptr socketDescriptor)
{
    qDebug() << socketDescriptor << " Connecting...";
    MyThread *thread = new MyThread(socketDescriptor, this);
    connect(thread, SIGNAL(signalData(qreal)), this, SLOT(onSignalData(qreal)), Qt::DirectConnection);
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    thread->start();
}

void MyServer::onSignalData(qreal data){
    qDebug()<<"слот сработал"<<data;
    emit signal1(data);
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QDebug>

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(qintptr ID, QObject *parent = 0);

    void run();

signals:
    void error(QTcpSocket::SocketError socketerror);
    void signalData(qreal);

public slots:
    void readyRead();
    void disconnected();

private:
    QTcpSocket *socket;
    qintptr socketDescriptor;
};

#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"

MyThread::MyThread(qintptr ID, QObject *parent) :
    QThread(parent)
{
    this->socketDescriptor = ID;
}

void MyThread::run()
{
    qDebug() << " Thread started";
    socket = new QTcpSocket();
    if(!socket->setSocketDescriptor(this->socketDescriptor))
    {
        emit error(socket->error());
        return;
    }
    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
    qDebug() << socketDescriptor << " Client connected";
    exec();
}

void MyThread::readyRead()
{
    QByteArray Data = socket->readAll();
    qDebug() << socketDescriptor << " Data in: " << Data;
    emit signalData(Data.toFloat());
}

void MyThread::disconnected()
{
    qDebug() << socketDescriptor << " Disconnected";
    socket->deleteLater();
    exit(0);
}
Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

7
Алексей Внуков
  • 19 січня 2021 р. 11:16

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

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

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

        Алексей Внуков
        • 20 січня 2021 р. 07:49
        • (відредаговано)
        • Відповідь була позначена як рішення.

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

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

            Алексей Внуков
            • 20 січня 2021 р. 07:57

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

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

                Коментарі

                Only authorized users can post comments.
                Please, Log in or Sign up
                AD

                C++ - Тест 004. Указатели, Массивы и Циклы

                • Результат:50бали,
                • Рейтинг балів-4
                m
                • molni99
                • 26 жовтня 2024 р. 01:37

                C++ - Тест 004. Указатели, Массивы и Циклы

                • Результат:80бали,
                • Рейтинг балів4
                m
                • molni99
                • 26 жовтня 2024 р. 01:29

                C++ - Тест 004. Указатели, Массивы и Циклы

                • Результат:20бали,
                • Рейтинг балів-10
                Останні коментарі
                ИМ
                Игорь Максимов22 листопада 2024 р. 11:51
                Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                Evgenii Legotckoi
                Evgenii Legotckoi31 жовтня 2024 р. 14:37
                Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                A
                ALO1ZE19 жовтня 2024 р. 08:19
                Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                ИМ
                Игорь Максимов05 жовтня 2024 р. 07:51
                Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                d
                dblas505 липня 2024 р. 11:02
                QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                Тепер обговоріть на форумі
                Evgenii Legotckoi
                Evgenii Legotckoi24 червня 2024 р. 15:11
                добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                t
                tonypeachey115 листопада 2024 р. 06:04
                google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
                NSProject
                NSProject04 червня 2022 р. 03:49
                Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
                9
                9Anonim25 жовтня 2024 р. 09:10
                Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

                Слідкуйте за нами в соціальних мережах