问题:这两天遇到项目程序莫名其妙的运行异常,那么可能需要自动终结该进程并重启。
思路:为了判断主程序是否正常运行,我们可以这样做,写一个辅助程序跟主程序通讯,这里采用本地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爱好者学习交流。