如何在非 gui 线程上正确创建 QUdpSocket? Readyread 未发出
How to properly create QUdpSocket on non-gui thread ? Readyread not emitted
我正在编写一个包含 QUdpSocket
:
的网络库
QAbstractSocket *UdpNetworkStreamer::addConnection()
{
QUdpSocket *udpSocket = new QUdpSocket(this);
udpSocket->bind(connection.port, QUdpSocket::ShareAddress);
bool ret = udpSocket->joinMulticastGroup(QHostAddress(connection.ip));
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::QueuedConnection);
return udpSocket;
}
- 创建一个新的
QUdpSocket
。
- 连接到其
readyRead
信号。
- 在引发
readyRead
时调用 readDatagram
。
当我从 Qt GUI 应用程序使用该库时一切正常。
当另一个用户包含在 Qt GUI 应用程序之外使用的库时,问题就开始了。
他调用 addConnection
(创建套接字并在 readyRead
上调用连接)
调用 addConnection
的线程是非 Qt。
addConnection
似乎成功结束,但 readyRead
从未发出。
调用读取(即使没有发出 readyRead
)导致数据报读取成功。
修复无效:
将 UDP 套接字线程移动到 this->thread
QUdpSocket *udpSocket = new QUdpSocket();
udpSocket->moveToThread(this->thread());
udpSocket->setParent(this);
我尝试通过calling:void
来模拟问题
MainWindow::on__btnOpenMulticastReceiver_clicked()
{
QFuture<void> future = QtConcurrent::run(this,
&MainWindow::CreateMulticastConnection, testHandle);
}
这也导致了与用户在我的图书馆中遇到的相同的症状,这意味着 readyRead
没有发出。
QSignalSpy
- 我在 readyRead
信号上激活了一个间谍;尽管我可以直接从套接字读取数据,但计数器一直为零。当使用套接字在主线程上初始化时,间谍给出了有效结果(即进展)。
我的问题:
- 我错过了什么和做错了什么?
- 发出
readyRead
的最简单方法是什么,即使它不是在主 GUI 线程上创建的 - 我找不到任何在没有 GUI 或外部 Qt 线程的情况下工作的示例。
我的猜测是您需要将 Q_OBJECT
宏添加到您需要发出信号的 class 的开头。除非你这样做,否则信号槽机制将无法正常工作。
我最终以这种方式解决了问题:
void MainWindow::OpenConnection()
{
QThread *t = new QThread();
t->start();
SocketWrapper *w= new SocketWrapper();
w->moveToThread(t);
w->metaObject()->invokeMethod(w, "CreateSocket", Qt::QueuedConnection);
}
您必须在创建套接字时使用套接字包装器 movedTo()
的线程调用 invokeMethod()
,以便创建套接字的线程将具有 运行 事件循环。
除此之外,CreateSocket()
需要成为 SocketWrapper
中的一个插槽,例如:
class SocketWrapper : public QObject
{
Q_OBJECT
public:
explicit SocketWrapper(QObject *parent = 0);
signals:
public slots:
void readyRead();
void CreateSocket();
private:
QUdpSocket *_socket;
};
我正在编写一个包含 QUdpSocket
:
QAbstractSocket *UdpNetworkStreamer::addConnection()
{
QUdpSocket *udpSocket = new QUdpSocket(this);
udpSocket->bind(connection.port, QUdpSocket::ShareAddress);
bool ret = udpSocket->joinMulticastGroup(QHostAddress(connection.ip));
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::QueuedConnection);
return udpSocket;
}
- 创建一个新的
QUdpSocket
。 - 连接到其
readyRead
信号。 - 在引发
readyRead
时调用readDatagram
。
当我从 Qt GUI 应用程序使用该库时一切正常。
当另一个用户包含在 Qt GUI 应用程序之外使用的库时,问题就开始了。
他调用 addConnection
(创建套接字并在 readyRead
上调用连接)
调用 addConnection
的线程是非 Qt。
addConnection
似乎成功结束,但 readyRead
从未发出。
调用读取(即使没有发出 readyRead
)导致数据报读取成功。
修复无效:
将 UDP 套接字线程移动到 this->thread
QUdpSocket *udpSocket = new QUdpSocket(); udpSocket->moveToThread(this->thread()); udpSocket->setParent(this);
我尝试通过calling:void
来模拟问题MainWindow::on__btnOpenMulticastReceiver_clicked() { QFuture<void> future = QtConcurrent::run(this, &MainWindow::CreateMulticastConnection, testHandle); }
这也导致了与用户在我的图书馆中遇到的相同的症状,这意味着
readyRead
没有发出。QSignalSpy
- 我在readyRead
信号上激活了一个间谍;尽管我可以直接从套接字读取数据,但计数器一直为零。当使用套接字在主线程上初始化时,间谍给出了有效结果(即进展)。
我的问题:
- 我错过了什么和做错了什么?
- 发出
readyRead
的最简单方法是什么,即使它不是在主 GUI 线程上创建的 - 我找不到任何在没有 GUI 或外部 Qt 线程的情况下工作的示例。
我的猜测是您需要将 Q_OBJECT
宏添加到您需要发出信号的 class 的开头。除非你这样做,否则信号槽机制将无法正常工作。
我最终以这种方式解决了问题:
void MainWindow::OpenConnection()
{
QThread *t = new QThread();
t->start();
SocketWrapper *w= new SocketWrapper();
w->moveToThread(t);
w->metaObject()->invokeMethod(w, "CreateSocket", Qt::QueuedConnection);
}
您必须在创建套接字时使用套接字包装器 movedTo()
的线程调用 invokeMethod()
,以便创建套接字的线程将具有 运行 事件循环。
除此之外,CreateSocket()
需要成为 SocketWrapper
中的一个插槽,例如:
class SocketWrapper : public QObject
{
Q_OBJECT
public:
explicit SocketWrapper(QObject *parent = 0);
signals:
public slots:
void readyRead();
void CreateSocket();
private:
QUdpSocket *_socket;
};