Qt QWebsocket :: open block пользовательский интерфейс - PullRequest
0 голосов
/ 22 ноября 2018

Я работаю на небольшой системе, она сделана несколькими клиентами и одним приложением администратора.У каждого клиента есть сервер QWebSocket для прослушивания запросов администратора, поэтому приложение администратора должно подключаться к различным клиентам.

Это мой диалог входа в систему:

Login dialog

Перед входом яЯ не знаю, какой IP-адрес клиента, поэтому каждый раз, когда я отправляю учетные данные, мне нужно пытаться открыть соединение с этим IP-адресом.Проблема в том, что в блоках пользовательского интерфейса Windows до тех пор, пока сервер сокетов не ответит или тайм-аут не достигнут, но в Windows он работает нормально.

РЕДАКТ. 1: Я следовал Тун Ле Тхань предложениямпоэтому код включает в себя его советы.Теперь основная проблема заключается в том, что ConnectionHelper не может излучать какой-либо сигнал без получения QSocketNotifier: уведомители сокетов не могут быть включены или отключены из другого потока

У меня есть ConnectionHelper, который находится вПлата за отправку полученных данных в и из установщика WebSocket.

main.cpp

ConnectionHelper *helper = new ConnectionHelper();
LoginDialog dialog(helper);

QThread* thread = new QThread();
helper->moveToThread(thread);
thread->start();

dialog.show();
return a.exec();

Конструктор LoginDialog:

connect(helper, &ConnectionHelper::onConnectionError, this, &LoginDialog::onCxnError);
connect(helper, &ConnectionHelper::loginInformationReceived, this, &LoginDialog::onLoginInfo);
connect(helper, &ConnectionHelper::cxnEstablished, this, &LoginDialog::onConnected);

Слот для приема:

void LoginDialog::on_buttonBox_accepted()
{
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
    QString host = ui->lineEditServer->text();
    QString port = ui->lineEditPort->text();
    QString ws = "ws://" + host + ":" + port;
    helper->setUrl(QUrl(ws));
}

void ConnectionHelper::setUrl(QUrl url)
{
        if(!webSocket)
{
    webSocket = new QWebSocket();

    connect(webSocket, &QWebSocket::textMessageReceived, this, &ConnectionHelper::processTextMessage, Qt::QueuedConnection);
    connect(webSocket, &QWebSocket::binaryMessageReceived, this, &ConnectionHelper::processBinaryMessage);
    connect(webSocket, &QWebSocket::disconnected , this, &ConnectionHelper::socketDisconnected);

    connect(webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error)
            , this, [this](QAbstractSocket::SocketError error){
        Q_UNUSED(error)
        emit onConnectionError();
    });

    connect(webSocket, &QWebSocket::connected, this, [=]() {
        emit cxnEstablished();
    });

}
webSocket->open(url);
    webSocket->open(url);
}

void ConnectionHelper::processTextMessage(QString message)
{
    QJsonDocument response = QJsonDocument::fromJson(message.toUtf8());
    QJsonObject objResponse = response.object();

    QString action = objResponse[ACTION_KEY].toString();

    if (action == ACTION_LOGIN)
        emit loginInformationReceived(objResponse);
}

Я отключаю кнопку ОК, пока не получим какой-либо ответ, и он отлично работает в Linux, но в Windows весь блок пользовательского интерфейса и перестает отвечать до получения ответа.

Я также пытаюсь переместитьсяConnectionHelper экземпляр в другой поток, но я получил этот ответ: QSocketNotifier: Уведомления сокетов нельзя включить или отключить из другого потока

У меня нет идей, которые мне нужно найтиспособ сделать webSocket->open(url) асинхронным или что-нибудь в этом роде.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 23 ноября 2018

Я понимаю, что QWebSocket::open - это единственная асинхронная функция, которую я использую.Поэтому мне нужно иметь только два потока, прежде чем устанавливать URL-адрес и открывать сокет-соединение.

После ответов Тунг Ле Тхана и небольшого подвоха теперь все работает отлично.Мое решение состояло в том, чтобы вернуться к стандартной угрозе, как только соединение будет установлено, и начать излучать сигналы.

void ConnectionHelper::setUrl(QUrl url, QThread* thread)
{
    if(!webSocket)
    {
        webSocket = new QWebSocket();
        connect(webSocket, &QWebSocket::connected, this, [this, thread]() {
            this->moveToThread(thread);
            webSocket->moveToThread(thread);

            emit this->cxnEstablished();
        });

    }
    webSocket->open(url);
}

И теперь LoginDialog необходимо отправить свою нить

void LoginDialog::on_buttonBox_accepted()
{
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
    QString host = ui->lineEditServer->text();
    QString port = ui->lineEditPort->text();
    QString ws = "ws://" + host + ":" + port;
    QMetaObject::invokeMethod(  helper, "setUrl", Qt::QueueConnection,
                Q_ARG( QUrl, QUrl(ws)),
                Q_ARG( QThread*, QThread::currentThread())
    );
}
0 голосов
/ 22 ноября 2018

Ошибка:

QSocketNotifier: уведомления о сокете нельзя включить или отключить из другого потока

при попытке вызвать сетевую функцию непосредственно из другого потока (помощник и его webSocket были в другом потоке).Вместо этого используйте invokeMethod или signal / slot.

EDIT 1: фактически, webSocket был создан во время вызова конструктора ConnectionHelper и принадлежал основному потоку.MoveToThread не позволяет перемещать webSocket, если ConnectionHelper не был установлен в качестве его родителя.Чтобы избежать этого, webSocket должен быть инициализирован с ConnectionHelper в качестве родителя или когда поток уже был запущен.

ПРИМЕЧАНИЕ: , если ваше приложение завершает работу сразу после диалогаПринятый () сработал (главное окно закрыто), вы не можете видеть, как испускаются ваши сигналы.

ОБНОВЛЕНИЕ 2

    ConnectionHelper::ConnectionHelper(QObject *parent) : QObject(parent)
    {
        webSocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this);

        connect( webSocket, &QWebSocket::stateChanged, this, [=](QAbstractSocket::SocketState s){
            qDebug() << "Socket state changed : " << s;
        }  );
        connect( webSocket, &QWebSocket::connected, this, [=](){
            emit cxnOk();
            webSocket->sendTextMessage("HELLO");
        } );

        void (QWebSocket::*error_signal)(QAbstractSocket::SocketError err) = &QWebSocket::error;

        connect( webSocket, error_signal, this, [=](QAbstractSocket::SocketError err){
            qDebug() << "On socket error : " << err;
        }  );

        connect( webSocket, &QWebSocket::textMessageReceived, this, [=](QString s){
            qDebug() << "text message received: " << s;
        } );
    }

    void ConnectionHelper::setUrl(QUrl url)
    {
        if( webSocket->state() == QAbstractSocket::ConnectedState ){
            webSocket->close();
        }

        qDebug() << "Open URL: " << url; 
        webSocket->open( url );
    }

Инициализация экземпляра ConnectionHelper:

    QThread * pThread = new QThread();
    m_pHelper = new ConnectionHelper();

    connect( m_pHelper, &ConnectionHelper::cxnOk, this, &MainWindow::onConnectionConnected, Qt::QueuedConnection );

    m_pHelper->moveToThread( pThread );
    pThread->start();

Измените setUrl на slot, затем используйте invokeMethod для отправки команды экземпляру помощника.

    void MainWindow::on_pushButton_clicked()
    {
        QString ws = "ws://echo.websocket.org";
        QMetaObject::invokeMethod( m_pHelper, "setUrl", Qt::QueuedConnection, Q_ARG( QUrl, QUrl(ws) ) );
        qDebug() << "Invoke setUrl ended" ;

    }

    void MainWindow::onConnectionConnected()
    {
        qDebug() << "[MainWindow] On connection connected !!!!";
    }

РЕЗУЛЬТАТЫ:

    Invoke setUrl ended
    Open URL:  QUrl("ws://echo.websocket.org")
    Socket state changed :  QAbstractSocket::ConnectingState
    Socket state changed :  QAbstractSocket::ConnectedState
    [MainWindow] On connection connected !!!!
    text message received:  "HELLO"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...