在網絡傳輸中,會出現各種各樣的情況,在長鏈接的使用中斷網重連機制就顯得尤爲重要了。
一、需要斷網重連的情況
- 接收不到數據的情況(網絡閃斷)
- 接收到數據爲空
二、斷網檢查方式
- 通過接收到數據是否 <= 0 判斷,如果 <= 0 說明已經斷開連接了
- 判斷一段時間內是否有接收到數據(長鏈接一般有心跳包進行鏈接診斷)
三、代碼實現
.h部分
#ifndef TCPTHREAD_H
#define TCPTHREAD_H
#include <QObject>
#include <QTcpSocket>
#include <QThread>
class TcpThread : public QThread
{
Q_OBJECT
public:
TcpThread();
~TcpThread();
void startThread(const QString& ip, int port);
void stopThreaad();
protected:
virtual void run();
protected slots:
void onConnect();
void onDisConnect();
void onReadMsg();
void onSendTcp();
public:
int m_iSendData;
int m_iRecv_TimeOut;
private:
QTcpSocket* m_TcpSocket;
bool m_isThreaStopped;
bool m_isOkConect;
QString m_QStrSocketIp;
int m_nSockPort;
QByteArray m_datagram;
};
#endif // TCPTHREAD_H
.cpp部分
#include "tcpthread.h"
#include "qtimer.h"
TcpThread::TcpThread()
: m_iSendData(0)
{
qDebug()<<"11";
m_TcpSocket = nullptr ;
m_isThreaStopped = false;
m_isOkConect = false;
}
TcpThread::~TcpThread()
{
qDebug()<<"exit_1";
m_isThreaStopped = true;
qDebug()<<"exit_2";
quit();
qDebug()<<"exit_3";
wait();
qDebug()<<"exit_4";
}
//線程的run函數
void TcpThread::run()
{
bool b_recv_flag = false;
if (!m_TcpSocket)
{
m_TcpSocket = new QTcpSocket();
connect(m_TcpSocket, SIGNAL(readyRead()), this, SLOT(onReadMsg()),Qt::DirectConnection);//讓接受函數在run子線程中執行(發送者執行)
connect(m_TcpSocket, SIGNAL(connected()), this, SLOT(onConnect()));
connect(m_TcpSocket, SIGNAL(disconnected()), this, SLOT(onDisConnect()));
}
while (!m_isThreaStopped)
{
//檢測客戶端 socket指針是否爲空
if(b_recv_flag)
msleep(100);
if (!m_isOkConect)
{
// 終止當前連接並重置套接字(立即關閉套接字,丟棄寫緩衝區中的任何掛起數據)
m_TcpSocket->abort();
m_TcpSocket->connectToHost(m_QStrSocketIp, m_nSockPort);
//等待連接。。。延時三秒,三秒內連不上返回false
m_isOkConect = m_TcpSocket->waitForConnected(3000);
m_iRecv_TimeOut = -1;
}
if (!m_isOkConect)
{
msleep(1);
continue;
}
else
{
qDebug()<<"tcp_flag:"<<m_iRecv_TimeOut;
onSendTcp();
}
b_recv_flag = m_TcpSocket->waitForReadyRead(100);
if (b_recv_flag)
{
m_iRecv_TimeOut = 0;
}
else
{
m_iRecv_TimeOut++;
if(m_iRecv_TimeOut>150) m_iRecv_TimeOut=150;
}
if(m_iRecv_TimeOut >= 150)
{
m_iRecv_TimeOut = -1;
m_TcpSocket->disconnectFromHost();
}
}
qDebug()<<"exit_5";
m_TcpSocket->disconnectFromHost();
qDebug()<<"exit_6";
}
void TcpThread::onDisConnect()
{
//socket一旦斷開則自動進入這個槽函數
//通過把 m_isOkConect 設爲false,在socket線程的run函數中將會重新連接主機
qDebug()<<"socket is disconnect!"<<endl;
m_isOkConect = false;
m_iRecv_TimeOut = -1;
qDebug()<<"disconnect_tcp";
}
void TcpThread::startThread(const QString& ip, int port )
{
m_QStrSocketIp = ip;
m_nSockPort = port;
m_isThreaStopped = false;
start();
}
void TcpThread::stopThreaad()
{
qDebug()<<"stop :3";
m_isThreaStopped = true;
qDebug()<<"stop :4";
qDebug()<<"stop :5";
}
void TcpThread::onConnect()
{
//已連接
}
void TcpThread::onReadMsg()
{
if(m_TcpSocket->bytesAvailable() <= 0)
{
// 判定連接失敗
m_TcpSocket->disconnectFromHost();
}
while (m_TcpSocket->bytesAvailable() > 0)
{
// 接收數據
m_datagram.clear();
m_datagram.resize(m_TcpSocket->bytesAvailable());
m_TcpSocket->read(m_datagram.data(), m_datagram.size());
QString str_tcp_receive = QString::fromLocal8Bit(m_datagram);
msleep(100);
}
}
void TcpThread::onSendTcp()
{
if((!m_TcpSocket)||m_TcpSocket->state()!=QAbstractSocket::ConnectedState)
return;
QString str_info = "";
m_iSendData = m_TcpSocket->write(str_info.toStdString().c_str(), strlen(str_info.toStdString().c_str()));
m_TcpSocket->flush();
if (m_iSendData < 0)
{
m_TcpSocket->disconnectFromHost();
return;
}
msleep(1000);
}
代碼介紹,如果是因爲沒有收到數據,大約15秒後會自動重連,如果是因爲接受到數據 <= 0,那麼會立即重連
這一篇有兩個潛在的bug:
(1)Linux下偶爾會有連不上情況,且會導致內存無限加大
(2)服務器端物理斷網後,當一旦連上服務器端會出現多次連接情況,但是客戶端實際上只發送了一次有效連接(不是特別明白出現原因)
在這一篇中解決了以上兩個問題(目前沒再見過):https://blog.csdn.net/bloke_come/article/details/105650204