Qt QWebsocket::open 块用户界面
Qt QWebsocket::open blocks user interface
我正在开发一个小型系统,它由多个客户和一个管理应用程序创建。每个客户端都有一个 QWebSocket 服务器来监听管理员的请求,因此管理员应用程序需要连接到不同的客户端。
这是我的登录对话框:
登录前,我不知道哪个是客户端 IP 地址,所以每次发送登录凭据时,我都需要尝试打开到该 IP 地址的连接。问题是在 Windows UI 中阻塞,直到套接字服务器响应或超时,但在 Windows 中它工作正常。
编辑 1: 我遵循了 Tung Le Thanh 的建议,因此代码包含了他的提示。现在的主要问题是 ConnectionHelper
无法在没有得到 QSocketNotifier 的情况下发出任何信号:无法从另一个线程启用或禁用套接字通知程序
我有一个 ConnectionHelper
负责向 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);
}
我会禁用“确定”按钮,直到收到任何响应并且在 Linux 上工作正常,但在 Windows 中整个 UI 块并变得无响应,直到收到响应。
我也尝试将 ConnectionHelper
实例移动到另一个线程,但我得到了这个响应:QSocketNotifier:无法从另一个线程启用或禁用套接字通知程序
我没有想法,我需要找到一种方法来使 webSocket->open(url)
异步或类似的东西。
谢谢。
错误:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
在您尝试直接从另一个线程调用网络函数时发生(助手及其 webSocket 在另一个线程中)。请改用 invokeMethod 或 signal/slot。
EDIT 1 : 实际上,webSocket 是在调用 ConnectionHelper 构造函数时创建的,它属于主线程。如果 ConnectionHelper 未设置为其父级,则 moveToThread 不允许移动 webSocket。为避免这种情况,必须使用 ConnectionHelper 作为父级 或在线程已经启动时 初始化 webSocket。
注意: 如果您的应用程序在对话框 accepted() 被触发(主要 window 关闭)后立即退出,您将看不到您发出的信号。
更新 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 更改为插槽,然后使用 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"
我意识到 QWebSocket::open
它是我使用的唯一异步函数。所以我只需要在设置 URL 和打开套接字连接之前有两个线程。
按照 Tung Le Thanh 的回答'和一个小技巧,现在一切正常。我的解决方案是在连接打开并开始发出信号后恢复默认威胁。
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())
);
}
我正在开发一个小型系统,它由多个客户和一个管理应用程序创建。每个客户端都有一个 QWebSocket 服务器来监听管理员的请求,因此管理员应用程序需要连接到不同的客户端。
这是我的登录对话框:
登录前,我不知道哪个是客户端 IP 地址,所以每次发送登录凭据时,我都需要尝试打开到该 IP 地址的连接。问题是在 Windows UI 中阻塞,直到套接字服务器响应或超时,但在 Windows 中它工作正常。
编辑 1: 我遵循了 Tung Le Thanh 的建议,因此代码包含了他的提示。现在的主要问题是 ConnectionHelper
无法在没有得到 QSocketNotifier 的情况下发出任何信号:无法从另一个线程启用或禁用套接字通知程序
我有一个 ConnectionHelper
负责向 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);
}
我会禁用“确定”按钮,直到收到任何响应并且在 Linux 上工作正常,但在 Windows 中整个 UI 块并变得无响应,直到收到响应。
我也尝试将 ConnectionHelper
实例移动到另一个线程,但我得到了这个响应:QSocketNotifier:无法从另一个线程启用或禁用套接字通知程序
我没有想法,我需要找到一种方法来使 webSocket->open(url)
异步或类似的东西。
谢谢。
错误:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
在您尝试直接从另一个线程调用网络函数时发生(助手及其 webSocket 在另一个线程中)。请改用 invokeMethod 或 signal/slot。
EDIT 1 : 实际上,webSocket 是在调用 ConnectionHelper 构造函数时创建的,它属于主线程。如果 ConnectionHelper 未设置为其父级,则 moveToThread 不允许移动 webSocket。为避免这种情况,必须使用 ConnectionHelper 作为父级 或在线程已经启动时 初始化 webSocket。
注意: 如果您的应用程序在对话框 accepted() 被触发(主要 window 关闭)后立即退出,您将看不到您发出的信号。
更新 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 更改为插槽,然后使用 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"
我意识到 QWebSocket::open
它是我使用的唯一异步函数。所以我只需要在设置 URL 和打开套接字连接之前有两个线程。
按照 Tung Le Thanh 的回答'和一个小技巧,现在一切正常。我的解决方案是在连接打开并开始发出信号后恢复默认威胁。
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())
);
}