QProcess的应用:杀死固定进程并重启进程

问题:这两天遇到项目程序莫名其妙的运行异常,那么可能需要自动终结该进程并重启。
思路:为了判断主程序是否正常运行,我们可以这样做,写一个辅助程序跟主程序通讯,这里采用本地udp socket的方式保持联系。主程序向辅助程序定时发送确认包。当辅助程序检测到主程序在一定时间内(假设是3s)没有发送数据包,那么辅助程序则判定主程序异常,先尝试性杀死进程,并自动重启。在这里考虑到这个程序是一个辅助程序,那么就打算把他做成一个后台服务程序。
先看下辅助程序:
//main.cpp

#include "Widget.h"
#include 
#include 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QProcess::startDetached("./xxx.exe");
    Widget w;
    return a.exec();
}

//widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 
#include 
#include 
#include 
#include 
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void slotReadyRead();
    void slotTimeout();

private:
    void initClass();
    void killProcess();
    void closeEvent(QCloseEvent*);

private:
    QUdpSocket *udpSocket;
    QTimer *timer;
    QProcess process;
    QWidget *widget;

private:
    Ui::Widget *ui;

};

#endif // WIDGET_H

//widget.cpp

#include "Widget.h"
#include "ui_Widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->plainTextEdit->appendPlainText("主程序运行中");
    this->setWindowFlags(windowFlags() &~ (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint));
    this->hide();
    initClass();
}

Widget::~Widget()
{
    udpSocket->close();
    udpSocket->deleteLater();
    udpSocket = nullptr;
    timer->stop();
    timer->deleteLater();
    timer = nullptr;
    delete ui;
}
void Widget::slotReadyRead()
{
    while(udpSocket->hasPendingDatagrams()){
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        QHostAddress senderHostAddress;
        quint16 senderPort;
        udpSocket->readDatagram(datagram.data(),datagram.size(),&senderHostAddress,&senderPort);
        qDebug()<<"UdpSocket Recevie:"<start(3000);
}

//定时如果到了,杀死进程并重启
void Widget::slotKillProcess()
{
    this->show();
    ui->plainTextEdit->appendPlainText("程序异常!!!");
    ui->plainTextEdit->appendPlainText("正在解决异常......");
    timer->stop();
    killProcess();
    ui->plainTextEdit->appendPlainText("正在重新启动......");
    QProcess::startDetached("./xxx.exe");
    this->hide();
}

void Widget::initClass()
{
    udpSocket = new QUdpSocket(this);
    if(udpSocket->bind(QHostAddress::LocalHost,20001,QAbstractSocket::ShareAddress)){
        qDebug()<<"绑定20001端口成功";
    }else{
        qDebug()<<"绑定20001端口失败";
    }
    connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::slotReadyRead);
    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,this,&Widget::slotKillProcess);
}

void Widget::killProcess()
{
    ui->plainTextEdit->appendPlainText("正在杀死进程xxx.exe");
    QStringList args;
    args<<"/F"<<"/IM"<<"xxx.exe"<<"/T";
    process.start(QString("TASKKILL"),args);
    process.waitForStarted();
    process.waitForFinished(2000);
    ui->plainTextEdit->appendPlainText("已经杀死进程xxx.exe");
}

void Widget::closeEvent(QCloseEvent *e)
{
    e->ignore();
}

如程序所见,我这里是先启动辅助程序,并会自动启动主程序,并等待主程序发送数据包,当第一个数据包发送过来,则启动定时器,如果三秒之内主程序没有再发送数据包到此端口,那么则调用slotKillProcess()方法,先杀死进程并再重启程序。为了让程序在后台运行,这里调用窗体类的hide();当异常时,再显示窗口,待主程序启动完成后再次回到后台。为了显示进程消息,这里在UI文件放了个plainTextEdit。
`
下面是主程序与辅助程序相应的通讯类
//AckNormal.h

#ifndef ACKNORMAL_H
#define ACKNORMAL_H

#include 
#include 
#include 
#include 

class AckNormal : public QWidget
{
    Q_OBJECT
public:
    explicit AckNormal(QWidget *parent = 0);
    ~AckNormal();

signals:

private slots:
    void slotTimeout();
private:
    void initClass();

private:
    QUdpSocket *udpSocket;
    QTimer *timer;
};

#endif // ACKNORMAL_H

//AckNormal.cpp

#include "AckNormal.h"

AckNormal::AckNormal(QWidget *parent) : QWidget(parent)
  ,udpSocket(nullptr),timer(nullptr)
{
    initClass();
}

AckNormal::~AckNormal()
{
    udpSocket->close();
    udpSocket->deleteLater();
    udpSocket = nullptr;
    timer->stop();
    timer->deleteLater();
    timer = nullptr;
}

void AckNormal::slotTimeout()
{
    QByteArray ba("online");
    udpSocket->writeDatagram(ba,QHostAddress::LocalHost,20001);
}

void AckNormal::initClass()
{
    udpSocket = new QUdpSocket(this);
    if(!udpSocket->bind(QHostAddress::LocalHost,20000,QAbstractSocket::ShareAddress)){
        QMessageBox::information(this,"Warning","绑定20000端口失败");
        return;
    }
    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,this,&AckNormal::slotTimeout);
    timer->start(1000);
}

如上面程序所见,主程序启动后实例化AckNormal,会自动向辅助程序发送确认数据包。
至此一个简单的辅助程序就这样完成了。欢迎与各位Qt爱好者学习交流。

你可能感兴趣的:(通讯,udp,socket,异常,Qt)