目錄
前言:
一、Qt直接啟動(dòng)本身的KeepAlive
二、在應(yīng)用層自己實(shí)現(xiàn)一個(gè)心跳檢測(cè)
?三、自定義心跳代碼實(shí)現(xiàn):
完整客戶端服務(wù)端工程下載:
共用的結(jié)構(gòu)體相關(guān)頭文件:
? ? ? ? 客戶端部分核心代碼:
? ? ? ? 服務(wù)端部分核心代碼:
運(yùn)行結(jié)果展示:
前兩篇關(guān)于qt tcp 相關(guān)的,可以通過(guò)以下傳送門查看:
Qt TCP相關(guān)的一些整理:客戶端常見(jiàn)操作 socket 通信 network-CSDN博客
Qt TCP相關(guān)的一些整理:服務(wù)端常見(jiàn)操作 socket 通信 network-CSDN博客
前言:
? ? ? ? TCP本身是有一個(gè)?;顮顟B(tài)的 keep-alive機(jī)制,默認(rèn)是關(guān)閉的,需要單獨(dú)啟動(dòng)就可以;默認(rèn)保活時(shí)間是2小時(shí),不過(guò)這個(gè)機(jī)制是在協(xié)議層,也就是傳輸層生效的,如果應(yīng)用層出問(wèn)題了,就不能及時(shí)發(fā)現(xiàn)問(wèn)題;如果想要實(shí)現(xiàn)斷線重連的操作,這個(gè)就不好實(shí)現(xiàn)了。
? ? ? ? 另一種方式,可以在應(yīng)用層自定義模擬這個(gè)心跳檢測(cè)機(jī)制,使用線程或者定時(shí)器來(lái)定時(shí)發(fā)心跳包即可實(shí)現(xiàn)?;罟δ?,并且能做到斷線重連的操作。
一、Qt直接啟動(dòng)本身的KeepAlive
m_client = new QTcpSocket(this);
// 啟動(dòng)心跳檢測(cè)
m_client->setSocketOption(QAbstractSocket::KeepAliveOption,true);
m_client->connectToHost("127.0.0.1",8898);
二、在應(yīng)用層自己實(shí)現(xiàn)一個(gè)心跳檢測(cè)
? ? ? ? 客戶端部分:需要使用定時(shí)器來(lái)定時(shí)發(fā)送心跳包,并且根據(jù)間隔和?;羁倳r(shí)長(zhǎng)來(lái)設(shè)定一個(gè)閾值次數(shù),一開(kāi)始按最大時(shí)長(zhǎng)的次數(shù)來(lái)初始化,當(dāng)不停的遞減閾值為0時(shí),就需要斷線,如果需要重連,那就重新連接一下;當(dāng)然收到包時(shí),需要重置閾值;
? ? ? ? 服務(wù)端部分:需要使用一個(gè)map容器來(lái)保存已經(jīng)連上的套接字及閾值,起一條線程來(lái)定時(shí)輪詢?nèi)萜鳎?dāng)發(fā)現(xiàn)閾值為0時(shí)則斷線,并且從容器中刪除鍵值對(duì);當(dāng)新的客戶端連接成功連上時(shí),要增加新的鍵值對(duì)到map容器中;當(dāng)收到數(shù)據(jù)包時(shí),要對(duì)閾值進(jìn)行重置;
?三、自定義心跳代碼實(shí)現(xiàn):
完整客戶端服務(wù)端工程下載:
點(diǎn)我下載
共用的結(jié)構(gòu)體相關(guān)頭文件:
struct_data.h 源碼
#ifndef STRUCT_DATA_H
#define STRUCT_DATA_H
enum TypeInfo{
HEART_CHECK_REQ, // 心跳檢測(cè)請(qǐng)求
HEART_CHECK_RES, // 心跳檢測(cè)響應(yīng)
};
struct Head
{
int type;
int len;
};
struct HeartCheckReq
{
Head head;
HeartCheckReq() {
head.type = HEART_CHECK_REQ;
head.len = sizeof(HeartCheckReq);
}
};
struct HeartCheckRes
{
Head head;
HeartCheckRes() {
head.type = HEART_CHECK_RES;
head.len = sizeof(HeartCheckRes);
}
};
#endif // STRUCT_DATA_H
? ? ? ? 客戶端部分核心代碼:
頭文件代碼:?
#ifndef TCPMAINWINDOW_H
#define TCPMAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QTimer>
#include <QTime>
#include "struct_data.h"
namespace Ui {
class TcpMainWindow;
}
class TcpMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit TcpMainWindow(QWidget *parent = 0);
~TcpMainWindow();
private slots:
void myRead(); // 收包槽函數(shù)
void on_pushButton_clicked();
void heartCheckSlot(); // 定時(shí)發(fā)心跳包的槽
private:
Ui::TcpMainWindow *ui;
QTcpSocket *m_client;
int m_heartCheckTimes;
QTimer *m_checkTimer;
};
#endif // TCPMAINWINDOW_H
源文件代碼:
#include "tcpmainwindow.h"
#include "ui_tcpmainwindow.h"
#include <QDebug>
#define HEART_CHECK_TIMES 6 // ?;?0秒,每5秒發(fā)一次心跳包,閾值為6
TcpMainWindow::TcpMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::TcpMainWindow)
{
ui->setupUi(this);
m_client = new QTcpSocket(this);
// 啟動(dòng)tcp默認(rèn)的保活
//m_client->setSocketOption(QAbstractSocket::KeepAliveOption,true);
m_client->connectToHost("127.0.0.1",8898);
if(m_client->waitForConnected()){
qDebug()<<"conn ok";
connect(m_client,SIGNAL(readyRead()),this,SLOT(myRead()));
m_heartCheckTimes = HEART_CHECK_TIMES; // 閾值初始化
// 心跳檢測(cè)相關(guān)的定時(shí)器 和 關(guān)聯(lián)操作
m_checkTimer = new QTimer(this);
connect(m_checkTimer,SIGNAL(timeout()),this,SLOT(heartCheckSlot()));
m_checkTimer->start(5000); // 5秒的間隔定時(shí)
}else{
qDebug()<<"conn fail"<<m_client->errorString();
}
}
TcpMainWindow::~TcpMainWindow()
{
delete ui;
}
void TcpMainWindow::myRead()
{
QByteArray buffer = m_client->readAll();
qDebug()<<buffer;
// 只是簡(jiǎn)單的打印輸出,還沒(méi)有做解包處理
m_heartCheckTimes = HEART_CHECK_TIMES;
}
void TcpMainWindow::on_pushButton_clicked()
{
char buffer[] = "碼蟻軟件歡迎您";
qDebug()<<m_client->write(buffer,sizeof(buffer));
}
void TcpMainWindow::heartCheckSlot()
{
HeartCheckReq req;
m_client->write((char*)&req,req.head.len); // 發(fā)送心跳包
m_heartCheckTimes--; // 遞減閾值
ui->textBrowser->append(QString("當(dāng)前時(shí)間:%1 心跳閾值為 %2").arg(QTime::currentTime().toString()).arg(m_heartCheckTimes));
if(m_heartCheckTimes <= 0){
// 需要做斷線重連操作
m_client->close();
m_client->connectToHost("127.0.0.1",8898);
if(m_client->waitForConnected()){
m_heartCheckTimes = HEART_CHECK_TIMES; // 重連成功,重置閾值
ui->textBrowser->append("重連成功");
}
}
}
? ? ? ? 服務(wù)端部分核心代碼:
頭文件代碼:?
#ifndef SERVERMAINWINDOW_H
#define SERVERMAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include "struct_data.h"
namespace Ui {
class ServerMainWindow;
}
class ServerMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit ServerMainWindow(QWidget *parent = 0);
~ServerMainWindow();
private slots:
void connectSlot(); // 處理連接的槽
void clientSlot(); // 與客戶端交互的槽
void checkTimer(); // 定時(shí)檢測(cè)心跳的槽
private:
Ui::ServerMainWindow *ui;
QTcpServer *m_server;
QMap<QTcpSocket*,int> m_clients;
QTimer *m_checkTimer;
};
#endif // SERVERMAINWINDOW_H
源文件代碼:
#include "servermainwindow.h"
#include "ui_servermainwindow.h"
#include <QDebug>
#define HEART_CHECK_TIMES 6
ServerMainWindow::ServerMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ServerMainWindow)
{
ui->setupUi(this);
m_server = new QTcpServer(this);
if(m_server->listen(QHostAddress::Any,8898)){
qDebug()<<"listen ok";
connect(m_server,SIGNAL(newConnection()),this,SLOT(connectSlot()));
// 心跳檢測(cè)相關(guān)的定時(shí)器及關(guān)聯(lián)操作
m_checkTimer = new QTimer(this);
connect(m_checkTimer,SIGNAL(timeout()),this,SLOT(checkTimer()));
m_checkTimer->start(5000);
}else{
qDebug()<<"listen fail"<<m_server->errorString();
}
}
ServerMainWindow::~ServerMainWindow()
{
delete ui;
}
void ServerMainWindow::connectSlot()
{
QTcpSocket *client = m_server->nextPendingConnection();
client->setSocketOption(QAbstractSocket::KeepAliveOption,true);
qDebug()<<client;
if(!client->isValid()) return;
m_clients[client] = HEART_CHECK_TIMES; // 用于心跳檢測(cè)的map
// 關(guān)聯(lián)與客戶端通信的自定義收包槽
connect(client,SIGNAL(readyRead()),this,SLOT(clientSlot()));
QByteArray buffer = "歡迎來(lái)到碼蟻軟件服務(wù)器。";
qDebug()<<client->write(buffer);
}
void ServerMainWindow::clientSlot()
{
QTcpSocket * client = static_cast<QTcpSocket *>(sender());
QByteArray buffer = client->readAll();
qDebug()<<buffer.data();
m_clients[client] = HEART_CHECK_TIMES; // 重置心跳閾值
int type = ((Head*)buffer.data())->type;
if(type == HEART_CHECK_REQ){
ui->textBrowser->append(QString("收到 %1 端口 %2 心跳包").arg(client->peerAddress().toString()).arg(client->peerPort()));
// 回一個(gè)響應(yīng)包
HeartCheckRes res;
client->write((char*)&res,res.head.len);
}
}
void ServerMainWindow::checkTimer()
{
for(auto it=m_clients.begin();it!=m_clients.end();){
it.value()--;
ui->textBrowser->append(QString("%1 %2 的閾值 %3").arg(it.key()->peerAddress().toString()).arg(it.key()->peerPort()).arg(it.value()));
if(it.value()==0){
ui->textBrowser->append(QString("發(fā)現(xiàn)無(wú)用連接 %1").arg(it.key()->peerAddress().toString()));
it.key()->close();
m_clients.erase(it++);
}else{
it++;
}
}
}
運(yùn)行結(jié)果展示:
當(dāng)關(guān)閉服務(wù)端之后,再重新開(kāi)啟服務(wù)端:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-751625.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-751625.html
到了這里,關(guān)于C++ Qt TCP的心跳檢測(cè)機(jī)制,斷線重連技術(shù),應(yīng)用層代碼重新實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!