QT 中Socket客户端与服务器异常断开后重连

    在现在的项目开发中,经常要使用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,然后我关掉服务器,于是客户端便进行重连...,后边重开服务器,客户端便重连成功。

上边是把定时器事件给屏蔽了,使用定时器来实现,也可以把定时器屏蔽,使用定时器事件进行重连,具体视项目情况而定。

猜你喜欢

转载自blog.csdn.net/lms1008611/article/details/81507161