在网络传输中,会出现各种各样的情况,在长链接的使用中断网重连机制就显得尤为重要了。
一、需要断网重连的情况
- 接收不到数据的情况(网络闪断)
- 接收到数据为空
二、断网检查方式
- 通过接收到数据是否 <= 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