繁体   English   中英

在Linux中使用ping操作的Qt线程

[英]Qt Thread with ping operation in Linux

我正在尝试完成看似非常简单的任务……但这只是一场噩梦。

我为Linux笔记本电脑开发了一个应用程序。 在应用程序内部,我希望有一个独立的线程可以连续ping另一台PC(例如,每5秒钟一次,直到永远……只要打开笔记本电脑,就可以)。

当然,当未连接到正在执行ping操作的PC时,该应用程序必须能够正常运行,而不必等待ping操作返回...如何实现此目的?

最初,我将QTimerQProcess:execute("ping"...) ,效果很好。 问题是,如果另一台PC没有响应,则每次ping操作时,我的整个应用程序及其GUI都会冻结大约一秒钟。 为了减少ping操作的等待时间,更改“ ping”选项(例如,设置“ -i0.2”)没有帮助:当未连接另一台PC时,我的应用程序变得非常慢。 如果我删除了ping,则当然一切都会顺利进行。

因此,我决定在QThread插入“ ping”操作,但是当我尝试按照http://doc.qt.io/qt-4.8/qthread.html中的第二个示例进行操作时,似乎没有任何效果:甚至没有开始。

这是代码:

//Pinger.h
class Pinger : public QThread
{
Q_OBJECT  
 void run();
public:
    void setParam(const QString &urlToPing); // it sets the url to ping

signals:
    /// \brief  Signal emitted when pinging of specified url fails
    void pingFailed(int ok);
private:
    QString pingurl;
};

//Pinger.cpp
void Pinger::run()
{
    int exitCode;
    QProcess pingProc;
    while(true)
    {
        exitCode=pingProc.execute("ping",QStringList() << "-c 1" << "-i0.2" << pingurl);
        emit pingFailed(exitCode);
        sleep(5);
    }
}

// MainWindow::MainWindow
        pinga= new Pinger(); // defined in mainwindow.h as Pinger* Pinga
        pinga->setParam(ip_address);
        connect(pinga,SIGNAL(pingFailed(int)),this,SLOT(connectionLost(int)));
        connect(pinga,SIGNAL(finished()),pinga,SLOT(deleteLater()));
        pinga->start();

有没有人尝试过类似的东西? 我对Qt还是很陌生,但是此操作似乎微不足道,以至于我发现无法实现它的方法令人难以置信。 所以我希望我只是缺少明显的东西。

如果使用QThread ,最好避免使用sleep(5); while(true)循环,因为无法杀死线程就不能正常关闭线程。 最好是通过在上一个任务(进程执行)完成时启动的单发计时器再次调用该周期性任务,而不是循环和阻止睡眠。 但是,在那种情况下,应该在其他成员插槽( Pinger::doWork() )中实现逻辑。 插槽run()应该与执行踩事件循环的默认实现保持一致。 可以通过将QThread::started()信号与Pinger::doWork()连接来开始工作:

connect(pinga, SIGNAL(started()), pinga, SLOT(doWork()));

需要注意删除QThread 通常, deleteLater()删除QThread对象( deleteLater() finished()信号中调用deleteLater()是不好的。 最好停止线程并在MainWindow析构函数中将其删除:

MainWindow::~MainWindow
{
    // stop the even loop
    pinga->quit();
    // wait for finishing current thread task; it can work only
    // if the thread is not blocked by while(true) with sleep
    pinga->wait();
    // delete if it is not a smart pointer
    delete pinga;
 }

在没有QThread情况下,也可以在主GUI线程中使用QProcess及其非阻塞API。 在那种情况下,它应该由QProcess::start() ,连接到信号QProcess::error()QProcess::finished()的插槽应用于启动下一个迭代。 这些插槽也不应阻塞主线程,因此,一旦上一个ping完成,则应使用QTimer开始下一个ping。

这是编写您的Pinger类的“ Qt方式”示例。 请注意,不需要线程。 QProcess异步使用,并通过Qt信号报告其状态。 一旦真正掌握了Qt,您就会意识到使用线程很少是解决这类问题的正确或最自然的解决方案。

请注意,我正在使用启用了C ++ 11支持的Qt 5将Qt信号连接到C ++ 11 lambdas ...您可以轻松地以Qt4样式编写它,但它不会那么紧凑且易读。

class Pinger : public QObject
{
    Q_OBJECT
public:
    Pinger(QObject *parent = 0) : QObject(parent)
    {
        //Have to do this ugliness because QProcess::finished is overloaded
        auto finishedFunc = static_cast<void(QProcess::*)(int)>(&QProcess::finished);
        connect(&m_process, finishedFunc, [this](int exitCode)
        {
            if( exitCode == 0 )
            {
                emit pingSuccess();
            }
            else
            {
                emit pingFailed(exitCode);
            }
        });
    }

    void run(const QString& hostToPing, int intervalInSeconds)
    {
        m_host = hostToPing;
        QTimer* timer = new QTimer(this);
        timer->start(intervalInSeconds * 1000);

        QObject::connect(timer, &QTimer::timeout, [this]()
        {
            if ( m_process.state() == QProcess::NotRunning )
            {
                m_process.start(QString("ping -c 1 -W 1 %1").arg(m_host));
            }
            else
            {
                qDebug() << "Cannot ping, previous ping operation still in progress!";
            }
        });
    }

signals:
    void pingSuccess();
    void pingFailed(int exitCode);

private:
    QProcess m_process;
    QString  m_host;
};

使用它很简单:

Pinger pinger;
QObject::connect(&pinger, &Pinger::pingSuccess, []()
{
    qDebug() << "Host is up!";
});

QObject::connect(&pinger, &Pinger::pingFailed, [](int exitCode)
{
    qDebug() << "Host is unreachable! Ping exit code = " << exitCode;
});

pinger.run("google.com", 3);

我遇到了同样的问题,并通过在自己的线程中运行ping命令来解决它。 通过使用信号,我正在与非阻塞GUI进行交互。

1)我声明了一个Ping类,该类运行Ping命令:

class Ping {
   public: 
      static bool start(QString host) {
        QStringList parameters;
        #if defined(WIN32)
           parameters << "-n" << "1";
        #else
           parameters << "-c 1";
        #endif

        parameters << host;

       int exitCode = QProcess::execute("ping", parameters);
       if (exitCode==0) {
           return true;
       } else {
           return false;
       }
    }
};

2.)另外,我还有一个NetworkReceiver类,该类通过Slot( startConnectionCheck )创建网络线程,并通过Signal( newConnectionStatus )与GUI交互:

class NetworkReceiver : public QObject
{
    Q_OBJECT
public:
    explicit NetworkReceiver(QObject * parent = nullptr);
    ~NetworkReceiver();

    void start() {
        while(true) {
            emit newConnectionStatus(Ping::start("google.at"));
            QThread::sleep(5);
        }
    }

signals:
    void newConnectionStatus(bool connected);

public slots:
    void startConnectionCheck() {
        QFuture<void> test = QtConcurrent::run(this, &NetworkReceiver::start);
    }

};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM