C++ QtQWebSocket::开放块用户界面

C++ QtQWebSocket::开放块用户界面,c++,qt,qtwebsockets,C++,Qt,Qtwebsockets,我正在开发一个小系统,它由几个客户端和一个管理应用程序组成。每个客户端都有一个QWebSocket服务器来监听管理员的请求,因此管理员应用程序需要连接到不同的客户端 这是我的登录对话框: 在登录之前,我不知道哪个是客户端ip地址,所以每次发送登录凭据时,我都需要尝试打开到该ip地址的连接。问题是,在Windows UI中,直到套接字服务器响应或达到超时,才会阻塞,但在Windows中,其工作正常 编辑1:我遵循了建议,因此代码中包含了他的提示。现在的主要问题是,ConnectionHelper

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

这是我的登录对话框:

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

编辑1:我遵循了建议,因此代码中包含了他的提示。现在的主要问题是,
ConnectionHelper
在没有获得QSocketNotifier的情况下无法发送任何信号:套接字通知程序无法从另一个线程启用或禁用

我有一个
ConnectionHelper
,负责向WebSocket设置器发送接收数据和从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);
}
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://”+主机+:“+端口;
助手->设置URL(QUrl(ws));
}
void ConnectionHelper::setUrl(QUrl url)
{
如果(!webSocket)
{
webSocket=新的QWebSocket();
连接(webSocket,&QWebSocket::textMessageReceived,this,&ConnectionHelper::processTextMessage,Qt::QueuedConnection);
连接(webSocket,&QWebSocket::binaryMessageReceived,this,&ConnectionHelper::processBinaryMessage);
连接(webSocket、&QWebSocket::disconnected、this、&ConnectionHelper::socketDisconnected);
连接(webSocket,QOverload::of(&QWebSocket::error)
,this,[this](QAbstractSocket::SocketError错误){
Q_未使用(错误)
发出onConnectionError();
});
连接(webSocket,&QWebSocket::connected,this,[=](){
发射CxNestabled();
});
}
webSocket->打开(url);
webSocket->打开(url);
}
无效ConnectionHelper::processTextMessage(QString消息)
{
QJsonDocument响应=QJsonDocument::fromJson(message.toUtf8());
QJsonObject objResponse=response.object();
QString action=objResponse[action_KEY].toString();
if(action==action\u登录)
发出loginInformationReceived(objResponse);
}
我会禁用OK按钮,直到收到任何响应,并且在Linux上工作正常,但在Windows中,整个UI会被阻塞,直到收到响应为止,都会变得无响应

我还尝试将
ConnectionHelper
实例移动到另一个线程,但得到了以下响应:QSocketNotifier:Socket通知程序无法从另一个线程启用或禁用

我没有主意了,我需要找到一种方法使
webSocket->open(url)
async或类似的东西

谢谢。

错误:

QSocketNotifier:无法从其他线程启用或禁用套接字通知程序

在尝试直接从另一个线程调用网络函数时发生(helper及其webSocket位于另一个线程中)。改用invokeMethod或signal/slot

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

注意:如果您的应用程序在启动accepted()对话框(主窗口关闭)后立即退出,则无法看到发出的信号

更新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 );
    }
将setUrl更改为slot,然后使用invokeMethod将命令发送到helper实例

    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 !!!!";
    }

我意识到,
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())
    );
}

您好,不确定这是否有帮助,但您尝试过吗?您好,您可以在lambda中捕获
,并使用
发出此->cxnestablish()
查看它是否有效。请注意,setUrl必须是invoke form invokeMethod,而不是从UI线程调用。不起作用。。。是的,我使用了您的建议
QMetaObject::invokeMethod(helper,“setUrl”,Qt::QueuedConnection,Q_ARG(QUrl,QUrl(ws))
我这样做了,但仍然有相同的警告
QSocketNotifier:Socket Notifier无法从另一个线程启用或禁用
在Windows上尝试过,没有锁定界面,但警告仍然存在。@Engel:检测到问题,我重新编辑了答案,请尝试一下,我在Windows和Linux上测试了解决方案,它可以工作@Thung Le Thanh我尝试过但没有成功,因为这个:connect(webSocket,&QWebSocket::connected,this,[=](){emit cxnestablish();});我无法发出该信号,我将尝试其他方法,但很难解决。您的解决方案解决了该问题,但不能解决整个问题,因为现在
ConnectionHelper
无法在没有警告的情况下发出任何信号:'(我很高兴您找到了解决方案。我也发布了我用于测试的解决方案。很高兴看到webSocket可以以这种方式移动到另一个线程。我认为找到了问题的根源,代码现在可以无需技巧地工作了。
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);
}
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())
    );
}