赞
踩
目录
前两篇关于qt tcp 相关的,可以通过以下传送门查看:
Qt TCP相关的一些整理:客户端常见操作 socket 通信 network-CSDN博客
Qt TCP相关的一些整理:服务端常见操作 socket 通信 network-CSDN博客
TCP本身是有一个保活状态的 keep-alive机制,默认是关闭的,需要单独启动就可以;默认保活时间是2小时,不过这个机制是在协议层,也就是传输层生效的,如果应用层出问题了,就不能及时发现问题;如果想要实现断线重连的操作,这个就不好实现了。
另一种方式,可以在应用层自定义模拟这个心跳检测机制,使用线程或者定时器来定时发心跳包即可实现保活功能,并且能做到断线重连的操作。
- m_client = new QTcpSocket(this);
- // 启动心跳检测
- m_client->setSocketOption(QAbstractSocket::KeepAliveOption,true);
- m_client->connectToHost("127.0.0.1",8898);
客户端部分:需要使用定时器来定时发送心跳包,并且根据间隔和保活总时长来设定一个阈值次数,一开始按最大时长的次数来初始化,当不停的递减阈值为0时,就需要断线,如果需要重连,那就重新连接一下;当然收到包时,需要重置阈值;
服务端部分:需要使用一个map容器来保存已经连上的套接字及阈值,起一条线程来定时轮询容器,当发现阈值为0时则断线,并且从容器中删除键值对;当新的客户端连接成功连上时,要增加新的键值对到map容器中;当收到数据包时,要对阈值进行重置;
struct_data.h 源码
- #ifndef STRUCT_DATA_H
- #define STRUCT_DATA_H
-
- enum TypeInfo{
- HEART_CHECK_REQ, // 心跳检测请求
- HEART_CHECK_RES, // 心跳检测响应
- };
-
- 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(); // 收包槽函数
-
- void on_pushButton_clicked();
-
- void heartCheckSlot(); // 定时发心跳包的槽
-
- 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 // 保活30秒,每5秒发一次心跳包,阈值为6
-
- TcpMainWindow::TcpMainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::TcpMainWindow)
- {
- ui->setupUi(this);
- m_client = new QTcpSocket(this);
- // 启动tcp默认的保活
- //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; // 阈值初始化
- // 心跳检测相关的定时器 和 关联操作
- m_checkTimer = new QTimer(this);
- connect(m_checkTimer,SIGNAL(timeout()),this,SLOT(heartCheckSlot()));
- m_checkTimer->start(5000); // 5秒的间隔定时
-
- }else{
- qDebug()<<"conn fail"<<m_client->errorString();
- }
- }
-
- TcpMainWindow::~TcpMainWindow()
- {
- delete ui;
- }
-
- void TcpMainWindow::myRead()
- {
- QByteArray buffer = m_client->readAll();
- qDebug()<<buffer;
- // 只是简单的打印输出,还没有做解包处理
-
- 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); // 发送心跳包
- m_heartCheckTimes--; // 递减阈值
- ui->textBrowser->append(QString("当前时间:%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("重连成功");
- }
- }
- }
头文件代码:
- #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(); // 定时检测心跳的槽
- 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()));
- // 心跳检测相关的定时器及关联操作
- 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; // 用于心跳检测的map
- // 关联与客户端通信的自定义收包槽
- connect(client,SIGNAL(readyRead()),this,SLOT(clientSlot()));
- QByteArray buffer = "欢迎来到码蚁软件服务器。";
- 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()));
- // 回一个响应包
- 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("发现无用连接 %1").arg(it.key()->peerAddress().toString()));
- it.key()->close();
- m_clients.erase(it++);
- }else{
- it++;
- }
- }
- }
当关闭服务端之后,再重新开启服务端:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。