如何在非 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;
}
  1. 创建一个新的 QUdpSocket
  2. 连接到其 readyRead 信号。
  3. 在引发 readyRead 时调用 readDatagram

当我从 Qt GUI 应用程序使用该库时一切正常。

当另一个用户包含在 Qt GUI 应用程序之外使用的库时,问题就开始了。

他调用 addConnection(创建套接字并在 readyRead 上调用连接)

调用 addConnection 的线程是非 Qt。

addConnection 似乎成功结束,但 readyRead 从未发出。

调用读取(即使没有发出 readyRead)导致数据报读取成功。

修复无效:

  1. 将 UDP 套接字线程移动到 this->thread

    QUdpSocket *udpSocket = new QUdpSocket();
    udpSocket->moveToThread(this->thread());
    udpSocket->setParent(this);
    
  2. 我尝试通过calling:void

    来模拟问题
    MainWindow::on__btnOpenMulticastReceiver_clicked()
    {
       QFuture<void> future = QtConcurrent::run(this, 
       &MainWindow::CreateMulticastConnection, testHandle);
    }
    

    这也导致了与用户在我的图书馆中遇到的相同的症状,这意味着 readyRead 没有发出。

  3. QSignalSpy - 我在 readyRead 信号上激活了一个间谍;尽管我可以直接从套接字读取数据,但计数器一直为零。当使用套接字在主线程上初始化时,间谍给出了有效结果(即进展)。

我的问题:

  1. 我错过了什么和做错了什么?
  2. 发出 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;
};