【问题标题】:How to properly create QUdpSocket on non-gui thread ? Readyread not emitted如何在非 gui 线程上正确创建 QUdpSocket? Readyread 未发出
【发布时间】:2017-08-30 15:45:45
【问题描述】:

我正在编写一个包装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上的connect)

调用addConnection的线程不是Qt。

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

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

无效的修复:

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

    QUdpSocket *udpSocket = new QUdpSocket();
    udpSocket->moveToThread(this->thread());
    udpSocket->setParent(this);
    
  2. 我试图通过调用来模拟问题:void

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

    这也导致了与用户使用我的库时相同的症状,这意味着 readyRead 没有发出。

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

我的问题:

  1. 我错过了什么和做错了什么?
  2. 发出readyRead 的最简单方法是什么,即使它不是在主 GUI 线程上创建的 - 我找不到任何适用于没有 GUI 或外部 Qt 线程的示例。

【问题讨论】:

  • 用户创建QUdpSocket 的线程是否有活动的事件循环?将连接类型更改为Qt::DirectConnection(只是通过快速测试)会发生什么?
  • @G.M.试图使用直接连接它没有帮助。创建 udpsocket 的线程没有事件循环,它不是 qt 线程。
  • 如果UdpNetworkStreamer 所在的线程没有事件循环,那么队列中的信号永远无法传递给它。我怀疑 readyRead 信号 is 正在发出,但结果事件位于事件队列中,永远不会被处理。
  • 我明白你在说什么,但信号间谍计数器不会至少更新一次吗?它仍然为零...
  • 我很确定你需要一个事件循环来发射readyRead。如果没有触发它的事件,它会发出什么?

标签: c++ multithreading qt sockets qudpsocket


【解决方案1】:

我最终以这种方式解决了问题:

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

【讨论】:

  • 您应该能够简单地将线程的started() 连接到您的CreateSocket() - 显然在moveToThread()connect() 之后才启动线程。
【解决方案2】:

我的猜测是您需要将Q_OBJECT 宏添加到您需要发出信号的类的开头。除非您这样做,否则信号槽机制将无法正常工作。

【讨论】:

  • 谢谢,但是我有那个宏,很明显,当我使用 qt gui 应用程序来使用库时,它工作得很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-10
  • 1970-01-01
相关资源
最近更新 更多