当前位置:   article > 正文

C++ Qt TCP的心跳检测机制,断线重连技术,应用层代码重新实现

C++ Qt TCP的心跳检测机制,断线重连技术,应用层代码重新实现

目录

前言:

一、Qt直接启动本身的KeepAlive

二、在应用层自己实现一个心跳检测

 三、自定义心跳代码实现:

完整客户端服务端工程下载:

共用的结构体相关头文件:

        客户端部分核心代码:

        服务端部分核心代码:

运行结果展示:


前两篇关于qt tcp 相关的,可以通过以下传送门查看:

Qt TCP相关的一些整理:客户端常见操作 socket 通信 network-CSDN博客

Qt TCP相关的一些整理:服务端常见操作 socket 通信 network-CSDN博客

前言:

        TCP本身是有一个保活状态的 keep-alive机制,默认是关闭的,需要单独启动就可以;默认保活时间是2小时,不过这个机制是在协议层,也就是传输层生效的,如果应用层出问题了,就不能及时发现问题;如果想要实现断线重连的操作,这个就不好实现了。

        另一种方式,可以在应用层自定义模拟这个心跳检测机制,使用线程或者定时器来定时发心跳包即可实现保活功能,并且能做到断线重连的操作。

一、Qt直接启动本身的KeepAlive

  1. m_client = new QTcpSocket(this);
  2. // 启动心跳检测
  3. m_client->setSocketOption(QAbstractSocket::KeepAliveOption,true);
  4. m_client->connectToHost("127.0.0.1",8898);

二、在应用层自己实现一个心跳检测

        客户端部分:需要使用定时器来定时发送心跳包,并且根据间隔和保活总时长来设定一个阈值次数,一开始按最大时长的次数来初始化,当不停的递减阈值为0时,就需要断线,如果需要重连,那就重新连接一下;当然收到包时,需要重置阈值;

        服务端部分:需要使用一个map容器来保存已经连上的套接字及阈值,起一条线程来定时轮询容器,当发现阈值为0时则断线,并且从容器中删除键值对;当新的客户端连接成功连上时,要增加新的键值对到map容器中;当收到数据包时,要对阈值进行重置;

 三、自定义心跳代码实现:

完整客户端服务端工程下载:

点我下载

共用的结构体相关头文件:

struct_data.h 源码
  1. #ifndef STRUCT_DATA_H
  2. #define STRUCT_DATA_H
  3. enum TypeInfo{
  4. HEART_CHECK_REQ, // 心跳检测请求
  5. HEART_CHECK_RES, // 心跳检测响应
  6. };
  7. struct Head
  8. {
  9. int type;
  10. int len;
  11. };
  12. struct HeartCheckReq
  13. {
  14. Head head;
  15. HeartCheckReq() {
  16. head.type = HEART_CHECK_REQ;
  17. head.len = sizeof(HeartCheckReq);
  18. }
  19. };
  20. struct HeartCheckRes
  21. {
  22. Head head;
  23. HeartCheckRes() {
  24. head.type = HEART_CHECK_RES;
  25. head.len = sizeof(HeartCheckRes);
  26. }
  27. };
  28. #endif // STRUCT_DATA_H

        客户端部分核心代码:

头文件代码: 

  1. #ifndef TCPMAINWINDOW_H
  2. #define TCPMAINWINDOW_H
  3. #include <QMainWindow>
  4. #include <QTcpSocket>
  5. #include <QTimer>
  6. #include <QTime>
  7. #include "struct_data.h"
  8. namespace Ui {
  9. class TcpMainWindow;
  10. }
  11. class TcpMainWindow : public QMainWindow
  12. {
  13. Q_OBJECT
  14. public:
  15. explicit TcpMainWindow(QWidget *parent = 0);
  16. ~TcpMainWindow();
  17. private slots:
  18. void myRead(); // 收包槽函数
  19. void on_pushButton_clicked();
  20. void heartCheckSlot(); // 定时发心跳包的槽
  21. private:
  22. Ui::TcpMainWindow *ui;
  23. QTcpSocket *m_client;
  24. int m_heartCheckTimes;
  25. QTimer *m_checkTimer;
  26. };
  27. #endif // TCPMAINWINDOW_H

源文件代码:

  1. #include "tcpmainwindow.h"
  2. #include "ui_tcpmainwindow.h"
  3. #include <QDebug>
  4. #define HEART_CHECK_TIMES 6 // 保活30秒,每5秒发一次心跳包,阈值为6
  5. TcpMainWindow::TcpMainWindow(QWidget *parent) :
  6. QMainWindow(parent),
  7. ui(new Ui::TcpMainWindow)
  8. {
  9. ui->setupUi(this);
  10. m_client = new QTcpSocket(this);
  11. // 启动tcp默认的保活
  12. //m_client->setSocketOption(QAbstractSocket::KeepAliveOption,true);
  13. m_client->connectToHost("127.0.0.1",8898);
  14. if(m_client->waitForConnected()){
  15. qDebug()<<"conn ok";
  16. connect(m_client,SIGNAL(readyRead()),this,SLOT(myRead()));
  17. m_heartCheckTimes = HEART_CHECK_TIMES; // 阈值初始化
  18. // 心跳检测相关的定时器 和 关联操作
  19. m_checkTimer = new QTimer(this);
  20. connect(m_checkTimer,SIGNAL(timeout()),this,SLOT(heartCheckSlot()));
  21. m_checkTimer->start(5000); // 5秒的间隔定时
  22. }else{
  23. qDebug()<<"conn fail"<<m_client->errorString();
  24. }
  25. }
  26. TcpMainWindow::~TcpMainWindow()
  27. {
  28. delete ui;
  29. }
  30. void TcpMainWindow::myRead()
  31. {
  32. QByteArray buffer = m_client->readAll();
  33. qDebug()<<buffer;
  34. // 只是简单的打印输出,还没有做解包处理
  35. m_heartCheckTimes = HEART_CHECK_TIMES;
  36. }
  37. void TcpMainWindow::on_pushButton_clicked()
  38. {
  39. char buffer[] = "码蚁软件欢迎您";
  40. qDebug()<<m_client->write(buffer,sizeof(buffer));
  41. }
  42. void TcpMainWindow::heartCheckSlot()
  43. {
  44. HeartCheckReq req;
  45. m_client->write((char*)&req,req.head.len); // 发送心跳包
  46. m_heartCheckTimes--; // 递减阈值
  47. ui->textBrowser->append(QString("当前时间:%1 心跳阈值为 %2").arg(QTime::currentTime().toString()).arg(m_heartCheckTimes));
  48. if(m_heartCheckTimes <= 0){
  49. // 需要做断线重连操作
  50. m_client->close();
  51. m_client->connectToHost("127.0.0.1",8898);
  52. if(m_client->waitForConnected()){
  53. m_heartCheckTimes = HEART_CHECK_TIMES; // 重连成功,重置阈值
  54. ui->textBrowser->append("重连成功");
  55. }
  56. }
  57. }

        服务端部分核心代码:

头文件代码: 

  1. #ifndef SERVERMAINWINDOW_H
  2. #define SERVERMAINWINDOW_H
  3. #include <QMainWindow>
  4. #include <QTcpServer>
  5. #include <QTcpSocket>
  6. #include <QTimer>
  7. #include "struct_data.h"
  8. namespace Ui {
  9. class ServerMainWindow;
  10. }
  11. class ServerMainWindow : public QMainWindow
  12. {
  13. Q_OBJECT
  14. public:
  15. explicit ServerMainWindow(QWidget *parent = 0);
  16. ~ServerMainWindow();
  17. private slots:
  18. void connectSlot(); // 处理连接的槽
  19. void clientSlot(); // 与客户端交互的槽
  20. void checkTimer(); // 定时检测心跳的槽
  21. private:
  22. Ui::ServerMainWindow *ui;
  23. QTcpServer *m_server;
  24. QMap<QTcpSocket*,int> m_clients;
  25. QTimer *m_checkTimer;
  26. };
  27. #endif // SERVERMAINWINDOW_H

源文件代码:

  1. #include "servermainwindow.h"
  2. #include "ui_servermainwindow.h"
  3. #include <QDebug>
  4. #define HEART_CHECK_TIMES 6
  5. ServerMainWindow::ServerMainWindow(QWidget *parent) :
  6. QMainWindow(parent),
  7. ui(new Ui::ServerMainWindow)
  8. {
  9. ui->setupUi(this);
  10. m_server = new QTcpServer(this);
  11. if(m_server->listen(QHostAddress::Any,8898)){
  12. qDebug()<<"listen ok";
  13. connect(m_server,SIGNAL(newConnection()),this,SLOT(connectSlot()));
  14. // 心跳检测相关的定时器及关联操作
  15. m_checkTimer = new QTimer(this);
  16. connect(m_checkTimer,SIGNAL(timeout()),this,SLOT(checkTimer()));
  17. m_checkTimer->start(5000);
  18. }else{
  19. qDebug()<<"listen fail"<<m_server->errorString();
  20. }
  21. }
  22. ServerMainWindow::~ServerMainWindow()
  23. {
  24. delete ui;
  25. }
  26. void ServerMainWindow::connectSlot()
  27. {
  28. QTcpSocket *client = m_server->nextPendingConnection();
  29. client->setSocketOption(QAbstractSocket::KeepAliveOption,true);
  30. qDebug()<<client;
  31. if(!client->isValid()) return;
  32. m_clients[client] = HEART_CHECK_TIMES; // 用于心跳检测的map
  33. // 关联与客户端通信的自定义收包槽
  34. connect(client,SIGNAL(readyRead()),this,SLOT(clientSlot()));
  35. QByteArray buffer = "欢迎来到码蚁软件服务器。";
  36. qDebug()<<client->write(buffer);
  37. }
  38. void ServerMainWindow::clientSlot()
  39. {
  40. QTcpSocket * client = static_cast<QTcpSocket *>(sender());
  41. QByteArray buffer = client->readAll();
  42. qDebug()<<buffer.data();
  43. m_clients[client] = HEART_CHECK_TIMES; // 重置心跳阈值
  44. int type = ((Head*)buffer.data())->type;
  45. if(type == HEART_CHECK_REQ){
  46. ui->textBrowser->append(QString("收到 %1 端口 %2 心跳包").arg(client->peerAddress().toString()).arg(client->peerPort()));
  47. // 回一个响应包
  48. HeartCheckRes res;
  49. client->write((char*)&res,res.head.len);
  50. }
  51. }
  52. void ServerMainWindow::checkTimer()
  53. {
  54. for(auto it=m_clients.begin();it!=m_clients.end();){
  55. it.value()--;
  56. ui->textBrowser->append(QString("%1 %2 的阈值 %3").arg(it.key()->peerAddress().toString()).arg(it.key()->peerPort()).arg(it.value()));
  57. if(it.value()==0){
  58. ui->textBrowser->append(QString("发现无用连接 %1").arg(it.key()->peerAddress().toString()));
  59. it.key()->close();
  60. m_clients.erase(it++);
  61. }else{
  62. it++;
  63. }
  64. }
  65. }

运行结果展示:

当关闭服务端之后,再重新开启服务端:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/395030
推荐阅读
相关标签
  

闽ICP备14008679号