Linux中具有ping操作的Qt线程

Qt Thread with ping operation in Linux

本文关键字:Qt 线程 操作 ping Linux      更新时间:2023-10-16

我正在努力完成一项看起来很简单的任务。。。但这反而是一场噩梦。

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

当然,当应用程序正在ping的PC没有连接时,应用程序必须顺利工作,而无需等待ping操作返回。。。我怎样才能做到这一点?

起初,我使用了QTimerQProcess:execute("ping"...),效果很好。问题是,如果另一台电脑没有回复,我的整个应用程序及其GUI在每次ping操作时都会冻结大约一秒钟。为了减少ping操作的等待时间,更改"ping"选项(例如设置"-i0.2")并没有帮助:当另一台电脑没有连接时,我的应用程序会变得非常慢。如果我去掉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时需要小心。通常,单独删除QThread对象(从其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,你就会意识到使用线程很少是解决这些类型问题的正确或最自然的方法。

请注意,我使用的Qt 5支持C++11,可以将Qt信号连接到C++11 Lambda。。。你可以很容易地用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类,它通过插槽(startConnectionCheck)创建网络线程,并通过信号(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);
    }
};