Qt QWebsocket :: open阻止用户界面

时间:2018-11-22 14:54:26

标签: c++ qt qtwebsockets

我正在一个小型系统上,它是由几个客户端和一个管理应用程序组成的。每个客户端都有一个QWebSocket服务器来侦听管理员的请求,因此管理员应用程序需要连接到不同的客户端。

这是我的登录对话框:

Login dialog

在登录之前,我不知道哪个是客户端IP地址,因此每次发送登录凭据时,我都需要尝试打开与该IP地址的连接。问题在于,在Windows UI阻塞之前,直到套接字服务器响应或超时为止,但在Windows中,它工作正常。

编辑1:我遵循了Tung Le Thanh的建议,因此代码中包含了他的提示。现在的主要问题是ConnectionHelper不能发出任何信号而没有得到 QSocketNotifier:无法从另一个线程启用或禁用套接字通知器

我有一个ConnectionHelper负责向WebSocket setver发送和从WebSocket setver发送接收数据。

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);
}

我确实禁用OK按钮,直到收到任何响应为止,并且在Linux上可以正常工作,但是在Windows上,整个UI块都会变得无响应,直到收到响应为止。

我也尝试将ConnectionHelper实例移动到另一个线程,但是得到了以下响应: QSocketNotifier:无法从另一个线程启用或禁用套接字通知程序 < / p>

我没有想法,我需要找到一种方法来使webSocket->open(url)异步或类似的事情。

谢谢。

2 个答案:

答案 0 :(得分:1)

我意识到QWebSocket::open是我正在使用的唯一异步函数。因此,在设置URL和打开套接字连接之前,我只需要两个线程即可。

以下是Tung Le Thanh的回答”,还有一点窍门,现在一切正常。我的解决方案是在连接打开后恢复为默认的Threat并开始发出信号。

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())
    );
}

答案 1 :(得分:0)

错误:

  

QSocketNotifier:套接字通知程序不能从另一个线程启用或禁用

尝试直接从另一个线程(帮助程序及其webSocket在另一个线程中)直接调用网络功能时发生

。请改用invokeMethod或signal / slot。

编辑1:实际上,webSocket是在调用ConnectionHelper构造函数时创建的,它属于主线程。如果未将ConnectionHelper设置为其父级,则moveToThread不允许移动webSocket。为避免这种情况,必须以ConnectionHelper作为父类或在线程已经启动时初始化webSocket。

注意::如果在触发对话框accept()(关闭主窗口)后立即退出应用程序,则看不到发出的信号。

更新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"