在现在的项目开发中,经常要使用TCP/IP协议来进行通讯,但有时候与服务器端的链接由于网络问题导致连接异常或断开,这就需要我们的软件能自动重连,在Linux中,我们的思维一般是通过心跳包来监控连接是否断开,有时候还单独开一个线程,但是在QT中,就变得简单多了,当连接异常断开时,会触发相应的信号,我们只要在这个信号对应的槽函数中做重连处理就可以了,不需要另开线程也不需要心跳包。由于网上查的QT重连服务器端,大多都是通过建立的线程去实现,我觉得比较麻烦,这里就记录下我自己的思路:使用定时器QTimer或定时器事件QTimerEvent,来实现重连,当当前的连接断开时,QT便会发送 disconnected()信号,我们在这个信号对应的槽函数中开启定时器,重连的操作就放在定时器对应的槽函数中执行(或重写的定时器事件中执行),当重连成功后,QT会发送connected()信号,此时我们在其对应的槽函数中关闭定时器即可。
下边直接来贴代码,我们是建了一个简单的clientt类,来做一个Socket客户端,首先是头文件client.h文件
#ifndef CLIENT_H
#define CLIENT_H
#include <QtWidgets/QWidget>
#include <QtNetwork>
#include <QtNetwork/QTcpSocket>
#include <QLineEdit>
#include <QTextEdit>
#include <QPushButton>
#include <QLabel>
#include <QTimerEvent>
#include <QTimer>
class Client : public QWidget
{
Q_OBJECT
public:
Client(QWidget *parent = 0);
~Client();
private:
//方式一:使用定时器事件实现socket重连
int m_TimerID; //定时器事件ID
void timerEvent(QTimerEvent * event); //需要重写改定时器事件函数
//方式二:使用定时器实现重连
QTimer m_Timer;
protected:
void init(); //UI初始化
void newTCPConnect(); //建立一个新连接
private slots:
void clientReadData(); //几个Socket连接相关的槽函数
void clientConnected();
void clientDisconnected();
void clientError(QAbstractSocket::SocketError socketError);
void clientStateChange(QAbstractSocket::SocketState socketState);
void onConnectButtonClicked();
void slotTimeOut();
private:
bool m_IsConnected;
QTcpSocket *tcpSocket;
QPushButton m_ConnectButton;
QLabel m_IPLabel;
QLabel m_PortLabel;
QLineEdit m_IPEdit;
QLineEdit m_PortEdit;
QTextEdit m_TextEdit;
};
#endif // CLIENT_H
下边是client.cpp文件
#include "Client.h"
#include <QHostAddress>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QDebug>
Client::Client(QWidget *parent) : QWidget(parent), m_IsConnected(false)
{
this->setFixedSize(500, 300);
init();
}
void Client::init()
{
m_IPLabel.setParent(this);
m_IPLabel.setText("IP");
m_PortLabel.setParent(this);
m_PortLabel.setText("Port");
m_IPEdit.setParent(this);
m_IPEdit.setText("127.0.0.1");
m_PortEdit.setParent(this);
m_PortEdit.setText("5000");
m_ConnectButton.setParent(this);
m_ConnectButton.setText("connect");
m_TextEdit.setParent(this);
QFormLayout *fLayout1 = new QFormLayout;
fLayout1->addRow(&m_IPLabel, &m_IPEdit);
QFormLayout *fLayout2 = new QFormLayout;
fLayout2->addRow(&m_PortLabel, &m_PortEdit);
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addLayout(fLayout1);
hLayout->addLayout(fLayout2);
hLayout->addWidget(&m_ConnectButton);
QVBoxLayout *vLayout = new QVBoxLayout;
vLayout->addLayout(hLayout);
vLayout->addWidget(&m_TextEdit);
this->setLayout(vLayout);
tcpSocket = new QTcpSocket(this);
connect(&m_ConnectButton, SIGNAL(clicked()), this, SLOT(onConnectButtonClicked()));
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(clientReadData()));
connect(tcpSocket, SIGNAL(connected()), this, SLOT(clientConnected()));
connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(clientStateChange(QAbstractSocket::SocketState)));
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(clientError(QAbstractSocket::SocketError)));
connect(&m_Timer, SIGNAL(timeout()), this, SLOT(slotTimeOut()));
}
void Client::newTCPConnect()
{
tcpSocket->abort();
QHostAddress address = QHostAddress(m_IPEdit.text());
unsigned int port = m_PortEdit.text().toInt();
tcpSocket->connectToHost(address, port);
if (!tcpSocket->waitForConnected(3000)) //连接时长为3s,超过3秒连不上则输出错误信息
{
}
}
//接收数据
void Client::clientReadData()
{
QString msg = "\nrecieve msg : " + tcpSocket->readAll();
m_TextEdit.append(msg);
tcpSocket->write("GET");
}
//连接成功是时调用
void Client::clientConnected()
{
m_Timer.stop(); //连接成功后定时器停止
//killTimer(m_TimerID); //连接成功后终止对应ID的定时器事件
m_IsConnected = true;
QString msg = "server IP : " + tcpSocket->peerAddress().toString();
m_TextEdit.append(msg);
msg = "server Port : " + QString::number(tcpSocket->peerPort());
m_TextEdit.append(msg);
m_ConnectButton.setText("disconnect");
m_ConnectButton.setStyleSheet("background-color: green");
//当重连成功时,可以根据具体情况恢复软件的一些工作
}
//连接断开时调用
void Client::clientDisconnected()
{
m_Timer.start(2000); //连接断开时开启定时器,定时时间为2s
//m_TimerID = startTimer(2000); //连接断开时开始一个定时器事件,并将ID赋给m_TimerID
m_IsConnected = false;
m_ConnectButton.setText("connect");
m_ConnectButton.setStyleSheet("background-color: red");
//当连接断开时,可以根据具体情况决定是否停止软件当前的一些工作,比方暂停测试什么的
}
//获取socket错误信息
void Client::clientError(QAbstractSocket::SocketError socketError)
{
m_IsConnected = false;
tcpSocket->close();
}
//连接状态发生改变时获取其状态
void Client::clientStateChange(QAbstractSocket::SocketState socketState)
{
#if 0
if (socketState == QAbstractSocket::UnconnectedState)
{
m_TextEdit.append("clientState : UnconnectedState");
}
else if (socketState == QAbstractSocket::HostLookupState)
{
m_TextEdit.append("clientState : HostLookupState");
}
else if (socketState == QAbstractSocket::ConnectedState)
{
m_TextEdit.append("clientState : ConnectedState");
}
else if (socketState == QAbstractSocket::ConnectingState)
{
m_TextEdit.append("clientState : ConnectingState");
}
else if (socketState == QAbstractSocket::ClosingState)
{
m_TextEdit.append("clientState : ClosingState");
}
#endif
}
void Client::onConnectButtonClicked()
{
m_TextEdit.append("");
if (m_ConnectButton.text() == "disconnect")
{
tcpSocket->disconnectFromHost();
}
else if(m_ConnectButton.text() == "connect")
{
newTCPConnect();
}
}
void Client::timerEvent(QTimerEvent * event)
{
if (event->timerId() == m_TimerID) //在定时器事件中重连服务器
{
if (!m_IsConnected)
{
m_TextEdit.append(QString::fromLocal8Bit("重连服务器..."));
newTCPConnect();
}
}
}
//使用定时器来重连服务器
void Client::slotTimeOut()
{
if (!m_IsConnected)
{
m_TextEdit.append(QString::fromLocal8Bit("重连服务器..."));
newTCPConnect();
}
}
Client::~Client()
{
}
主程序中直接使用这个类:
#include "Client.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Client w;
w.show();
return a.exec();
}
编译运行:
刚开始与服务器端正常连接并接收数据hello sokect,然后我关掉服务器,于是客户端便进行重连...,后边重开服务器,客户端便重连成功。
上边是把定时器事件给屏蔽了,使用定时器来实现,也可以把定时器屏蔽,使用定时器事件进行重连,具体视项目情况而定。