当前位置: 首页 > article >正文

qt tcpsocket编程遇到的并发问题

1. 单个socket中接收消息的方法要使用局部变量而非全局,避免消息频发时产生脏数据

优化后的关键代码

  • recieveInfo() 方法通过返回内部处理后的 msg 进行传递
  • if (data.indexOf("0103") == -1) { 这里增加了判断, 对数据(非注册和心跳)再进行一遍过滤
void CustomTcpSocket::handleReadyRead()
{// 信号灯闪烁emit SignalRelayer::instance()->msgInOut(true);// 接收到消息QString msg = recieveInfo();// 解析收到的数据parseRegisterMsg(msg.split(",")[0], msg.split(",")[1]);// 判断注册的设备号是否可用if(!Myapp::devEnabled(devNumber)) {return;}// 判断是否是业务数据if(isNotBusinessInfo()) return;// 不是0103开头的数据不处理QString data = msg.split(",")[1];if (data.indexOf("0103") == -1) {qDebug() << "不是0103开头数据不做处理, data: " << data;return;}// 电路数据处理handleReadReadImpl(data);
}

以下是socket自定义类源码.cpp

#include "customtcpsocket.h"#include <iostream>CustomTcpSocket::CustomTcpSocket(QObject *parent): QTcpSocket(parent)
{sendMsg = QString("01 03 00 00 00 06 C5 C8");// 监听获取设备数据connect(this, &QIODevice::readyRead, this, &CustomTcpSocket::handleReadyRead);// 监听采集频率变更connect(SignalRelayer::instance(), &SignalRelayer::socketIntervalTimeChanged, this, &CustomTcpSocket::handleChangeIntervalTime);connect(SignalRelayer::instance(), &SignalRelayer::socketDestroy, this, [=]{this->disconnectFromHost();});
}/*** @brief 控制器初始化*/
void CustomTcpSocket::controllerInit() {// 电流预警控制器初始化elecWarnController = new ElecStreamWarnController(this, this->devNumber, this->devName, this->speakRender);// 在线预警控制器onlineWarnController = new OnlineWarnController(this, this->devNumber, this->devName, this->speakRender);// 拉取数据预警控制器pullDataWarnController = new PullDataWarnController(this, this->devNumber, this->devName, this->speakRender);
}CustomTcpSocket::~CustomTcpSocket()
{qDebug() << "detete socket";
}void CustomTcpSocket::parseRegisterMsg(QString &asciiString, QString &data)
{// 若为注册记录设备id等信息if(asciiString.left(1).compare("*") == 0) {// 是注册消息, 更新dev_unit_online字段QString tmpDevNumber = asciiString.mid(1, 8);if (tmpDevNumber.compare(devNumber) == 0) {qDebug() << "设备已注册, 不再处理.";return;}// 判断此设备是否存在DevInfo devInfo = existDev(tmpDevNumber);if (devInfo.devNumber.compare("") == 0) {// 设备不存在return;}// 赋值通讯单元名称this->devName = devInfo.devName;devNumber = tmpDevNumber;// 初始化消息内容处理器this->speakRender = new DevSpeakRender(this, this->devName, this->peerPort(), this->clientIp);// 提取必要字符telephone = asciiString.replace(devNumber, "").replace("#", "").replace(",", "").replace("*", "");this->isRegisterMsg = true;emit SignalRelayer::instance()->consoleMsg(this->speakRender->consoleFullPrint(Myapp::COMM_DIRECT[1].toInt(), asciiString, "注册"));// 更新在线状态
//        Myapp::updateDevUnitOnline(this->devNumber, Myapp::ONLINE_STATUS[1]);// 发送心跳通知. 此处注册成功emit SignalRelayer::instance()->socketPublish(this->speakRender->consoleFullPrint(Myapp::COMM_DIRECT[2].toInt(), "设备注册成功, 心跳检测开启."));// 控制器初始化controllerInit();// 初始化拉取定时任务initPullTimer();// 发送重置用于在线检测emit SignalRelayer::instance()->flushOfflineDetectManager();return;}// 非注册不处理if (!isRegister()) {return;}if (asciiString.compare("&&&&") == 0) {// 触发心跳onlineWarnController->detectedBeat();this->isHeartMsg = true;// 接收消息打印emit SignalRelayer::instance()->consoleMsg((this->speakRender->consoleFullPrint(Myapp::COMM_DIRECT[1].toInt(), data, "收到心跳包")));return;} else {this->isHeartMsg = false;this->isRegisterMsg = false;emit SignalRelayer::instance()->consoleMsg((this->speakRender->consoleFullPrint(Myapp::COMM_DIRECT[1].toInt(), data, "接收消息")));}}void CustomTcpSocket::durationMsg(QString msg, int commDirt) {if (!isRegister()) {return;}emit SignalRelayer::instance()->transferMsgDuration(RegisterLogDto(this->devNumber, this->clientIp, this->peerPort(), msg, commDirt));
}CustomTcpSocket::DevInfo CustomTcpSocket::existDev(const QString &tmpDevNumber) {QString sql = QString("SELECT u.name, d.numb "" from dev_manage_ref c"" inner join dev_manage_ref e on e.pid = c.id"" inner join dev_manage_ref w on w.pid = e.id"" inner join dev_manage_ref u on u.pid = w.id"" inner join dev_unit d on d.id = u.id where d.numb = '%1' and d.enable = 1").arg(tmpDevNumber);QSqlQuery query;if (!query.exec(sql) || !query.next() || query.value(1).isNull()) {return DevInfo();}return DevInfo(query.value(0).toString(), query.value(1).toString());
}void CustomTcpSocket::handleReadyRead()
{// 信号灯闪烁emit SignalRelayer::instance()->msgInOut(true);// 接收到消息QString msg = recieveInfo();// 解析收到的数据parseRegisterMsg(msg.split(",")[0], msg.split(",")[1]);// 判断注册的设备号是否可用if(!Myapp::devEnabled(devNumber)) {return;}// 判断是否是业务数据if(isNotBusinessInfo()) return;// 不是0103开头的数据不处理QString data = msg.split(",")[1];if (data.indexOf("0103") == -1) {qDebug() << "不是0103开头数据不做处理, data: " << data;return;}// 电路数据处理handleReadReadImpl(data);
}QString CustomTcpSocket::recieveInfo()
{QByteArray byteArray = this->readAll();QString reciveInfoMsg = byteArray.toHex();qDebug() << "socket.devNumber: " << this->devNumber << ", devName: " << this->devName << ", message: " << reciveInfoMsg;// 持久化记录消息, 记录原始记录durationMsg(reciveInfoMsg, Myapp::COMM_DIRECT[1].toInt());byteArray.clear();byteArray = QByteArray::fromHex(reciveInfoMsg.toUtf8());asciiString = QString::fromUtf8(byteArray);byteArray.clear();return QString("%1,%2").arg(asciiString).arg(reciveInfoMsg);}bool CustomTcpSocket::isNotBusinessInfo()
{// 若是注册, 返回不是业务数据if (isRegisterMsg) {return true;}// 若是心跳, 更新在线状态if (isHeartMsg) {Myapp::updateDevUnitOnline(devNumber, Myapp::ONLINE_STATUS[1]);return true;}// 若拉取异常, 不处理业务数据, 接收的数据会异常
//    if (pullDataWarnController->pullDataEchoErrNotifyFlag) {
//        return true;
//    }return false;
}void CustomTcpSocket::handleReadReadImpl(QString &asciiString)
{// 转换十进制QString midstr = asciiString.mid(4,2);// 获取返回数据长度QByteArray arylen = QByteArray::fromHex(midstr.toLocal8Bit());// 获取三路电流数值, 得到10进制QString lenResult = hexToInt(arylen);// 判断是否异常更新设备// 获取 总功率值(在第3块(4个为一块)16进制截取4位)int ab = getIntResultByIndex(asciiString, 4);int bc = getIntResultByIndex(asciiString, 5);int ca = getIntResultByIndex(asciiString, 6);QString recordType;// 处理电流elecWarnController->handleData(ab, bc, ca);// 拉取正常进行处理pullDataWarnController->handleNormal();
}void CustomTcpSocket::initPullTimer() {pullDataTimer = new QTimer(this);connect(pullDataTimer, &QTimer::timeout, this, &CustomTcpSocket::handleWrite);pullDataTimer->start(Myapp::secToMillSec(Myapp::getValueByKey(Myapp::SERV_INTERVAL_TIME_KEY)));
}void CustomTcpSocket::handleWrite()
{// 设备ID为空则不拉取数据if (!isRegister()) {return;}// 判断前一次数据是否拉取成功.this->pullDataWarnController->echoDetect();QByteArray data = QByteArray::fromHex(sendMsg.toLocal8Bit());emit SignalRelayer::instance()->consoleMsg(this->speakRender->consoleFullPrint(Myapp::COMM_DIRECT[0].toInt(), this->sendMsg));// 持久化发送消息durationMsg(sendMsg, Myapp::COMM_DIRECT[0].toInt());// 向服务端发送this->write(data);
}
// ---------- 注册与注销bool CustomTcpSocket::isRegister() {return !devNumber.isNull() && !devNumber.isEmpty();
}/*** @brief 注销socket, 避免在断网瞬间还在接收消息, 但内容已经不正确, 读取不到数据了*/
void CustomTcpSocket::logOut() {this->devNumber = "";
}// ---------- 采集频率void CustomTcpSocket::handleChangeIntervalTime(int intervalTime)
{this->pullDataTimer->setInterval(Myapp::secToMillSec(intervalTime));emit SignalRelayer::instance()->consoleMsg(this->speakRender->consoleWrapperForDev(QString("采集频率重置为: %1 秒.").arg(intervalTime)));
}// --------- 解析设备回应的电流等数据↓int CustomTcpSocket::getIntResultByIndex(const QString &hexString, int index)
{QString wantStr = hexString.mid(3*2+((index-1)*4), 4);
//    qDebug() << "index: " << index << ", wantStr: " << wantStr;QByteArray arylen = QByteArray::fromHex(wantStr.toLocal8Bit());QString high = wantStr.left(2);QString low = wantStr.right(2);return 16*hexToInt(QByteArray::fromHex(high.toLocal8Bit())).toInt()+hexToInt(QByteArray::fromHex(low.toLocal8Bit())).toInt();
}QString CustomTcpSocket::hexToInt(const QByteArray &byteAry)
{QString result;for (int i = 0; i < byteAry.size(); i++){quint8 byte = byteAry[i];result += QString::number(byte, 10);}return result;
}

相关文章:

qt tcpsocket编程遇到的并发问题

1. 单个socket中接收消息的方法要使用局部变量而非全局&#xff0c;避免消息频发时产生脏数据 优化后的关键代码 recieveInfo() 方法通过返回内部处理后的 msg 进行传递if (data.indexOf("0103") -1) { 这里增加了判断, 对数据&#xff08;非注册和心跳&#xff0…...

zabbix监控网站(nginx、redis、mysql)

目录 前提准备&#xff1a; zabbix-server主机配置&#xff1a; 1. 安装数据库 nginx主机配置&#xff1a; 1. 安装nginx redis主机配置&#xff1a; 1. 安装redis mysql主机配置&#xff1a; 1. 安装数据库 zabbix-server&#xff1a; 1. 安装zabbix 2. 编辑配置文…...

蓝桥杯冲刺

例题1&#xff1a;握手问题 方法1&#xff1a;数学推理(简单粗暴&#xff09; 方法2&#xff1a;用代码实现方法1 #include<iostream> using namespace std; int main() {int result 0;for (int i 1; i < 49; i){for (int j i 1; j < 50; j){//第i个人与第j个…...

文心一言与 DeepSeek 的竞争分析:技术先发优势为何未能转化为市场主导地位?

目录 引言 第一部分&#xff1a;技术路径的差异——算法创新与工程优化的博弈 1.1 文心一言的技术积累与局限性 1.1.1 早期技术优势 1.1.2 技术瓶颈与局限性 1.2 DeepSeek 的技术突破 1.2.1 算法革命与工程创新 1.2.2 工程成本与效率优势 第二部分&#xff1a;生态策略…...

Spring Security(maven项目) 3.1.0

前言&#xff1a; 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往…...

合并两个有序数组(Java实现)

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&#xff0c;合并后数组…...

Tree - Shaking

Vue 3 的 Tree - Shaking 技术详解 Tree - Shaking 是一种在打包时移除未使用代码的优化技术&#xff0c;在 Vue 3 中&#xff0c;Tree - Shaking 发挥了重要作用&#xff0c;有效减少了打包后的代码体积&#xff0c;提高了应用的加载性能。以下是对 Vue 3 中 Tree - Shaking …...

C# 从代码创建选型卡+表格

private int tabNum 1; private int sensorNum 5; private void InitializeUI() {// 创建右侧容器面板Panel rightPanel new Panel{Dock DockStyle.Right,Width 300,BackColor SystemColors.ControlDark,Parent this};// 根据防区数量创建内容if (tabNum &g…...

OpenCV 从入门到精通(day_02)

1. 边缘填充 为什么要填充边缘呢&#xff1f;我们以下图为例&#xff1a; 可以看到&#xff0c;左图在逆时针旋转45度之后原图的四个顶点在右图中已经看不到了&#xff0c;同时&#xff0c;右图的四个顶点区域其实是什么都没有的&#xff0c;因此我们需要对空出来的区域进行一个…...

VTK的两种显示刷新方式

在类中先声明vtk的显示对象 vtkRenderer out_render; vtkVertexGlyphFilter glyphFilter; vtkPolyDataMapper mapper; // 新建制图器 vtkActor actor; // 新建角色 然后在init中先初始化一下&#xff1a; out_rend…...

Ceph异地数据同步之-RBD异地同步复制(上)

#作者&#xff1a;闫乾苓 文章目录 前言基于快照的模式&#xff08;Snapshot-based Mode&#xff09;工作原理单向同步配置步骤单向同步复制测试双向同步配置步骤双向同步复制测试 前言 Ceph的RBD&#xff08;RADOS Block Device&#xff09;支持在两个Ceph集群之间进行异步镜…...

【C++】STL库_stack_queue 的模拟实现

栈&#xff08;Stack&#xff09;、队列&#xff08;Queue&#xff09;是C STL中的经典容器适配器 容器适配器特性 不是独立容器&#xff0c;依赖底层容器&#xff08;deque/vector/list&#xff09;通过限制基础容器接口实现特定访问模式不支持迭代器操作&#xff08;无法遍历…...

前端对接下载文件接口、对接dart app

嵌套在dart app里面的前端项目 1.前端调下载接口 ->后端返回 application/pdf格式的文件 ->前端将pdf处理为blob ->blob转base64 ->调用dart app的 sdk saveFile ->保存成功 async download() {try {// 调用封装的 downloadEContract 方法获取 Blob 数据const …...

一周学会Pandas2 Python数据处理与分析-编写Pandas2 HelloWord项目

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们首先准备一个excel文件&#xff0c;用来演示pandas操作数据集(数据的集合)。excel文件属于数据集的一种&#xf…...

【易订货-注册/登录安全分析报告】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…...

AI赋能股票:流通股本与总股本:定义、区别及投资意义解析

一、基本定义 总股本&#xff08;Total Shares Outstanding&#xff09; 指一家公司已发行的所有股票数量&#xff0c;包括流通股和非流通股&#xff08;如限售股、员工持股计划股票等&#xff09;。总股本反映公司的整体股权结构&#xff0c;是计算市值&#xff08;总股本 股…...

如何在Windows上找到Python安装路径?两种方法快速定位

原文&#xff1a;如何在Windows上找到Python安装路径&#xff1f;两种方法快速定位 | w3cschool笔记 在 Windows 系统上找到 Python 的安装路径对于设置环境变量或排查问题非常重要。本文将介绍两种方法&#xff0c;帮助你找到 Python 的安装路径&#xff1a;一种是通过命令提…...

第五课:高清修复和放大算法

文章目录 Part.01 高清修复(Hi-Res Fix)Part.02 SD放大(SD Upscale)Part.03 附加功能放大Part.01 高清修复(Hi-Res Fix) 文生图中的高清修复/高分辨率修复/超分辨率修复先低分辨率抽卡,再高分辨率修复。不能突破显存限制放大重绘幅度安全范围是0.3-0.5,如果想让AI更有想象力0…...

lvgl避坑记录

一、log调试 #if LV_USE_LOG && LV_LOG_LEVEL > LV_LOG_LEVEL_INFOswitch(src_type) {case LV_IMG_SRC_FILE:LV_LOG_TRACE("lv_img_set_src: LV_IMG_SRC_FILE type found");break;case LV_IMG_SRC_VARIABLE:LV_LOG_TRACE("lv_img_set_src: LV_IMG_S…...

Java 8 的流(Stream API)简介

Java 8 引入的 Stream API 是一个强大的工具&#xff0c;用于处理集合&#xff08;如 List、Set&#xff09;中的元素。它支持各种操作&#xff0c;包括过滤、排序、映射等&#xff0c;并且能够以声明式的方式表达复杂的查询操作。流操作可以是中间操作&#xff08;返回流以便进…...

液态神经网络技术指南

一、引言 1.从传统神经网络到液态神经网络 神经网络作为深度学习的核心工具&#xff0c;在图像识别、自然语言处理、推荐系统等领域取得了巨大成功。尤其是卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;、长短期记忆网络&#xff08;LS…...

element-plus中,表单校验的使用

目录 一.案例1&#xff1a;给下面的表单添加校验 1.目的要求 2.步骤 ①给需要校验的el-form-item项&#xff0c;添加prop属性 ②定义一个表单校验对象&#xff0c;里面存放了每一个prop的检验规则 ③给el-form组件&#xff0c;添加:rules属性 ④给el-form组件&#xff0…...

PyTorch复现线性模型

【前言】 本专题为PyTorch专栏。从本专题开始&#xff0c;我将通过使用PyTorch编写基础神经网络&#xff0c;带领大家学习PyTorch。并顺便带领大家复习以下深度学习的知识。希望大家通过本专栏学习&#xff0c;更进一步了解人更智能这个领域。 材料来源&#xff1a;2.线性模型_…...

Kafka+Zookeeper从docker部署到spring boot使用完整教程

文章目录 一、Kafka1.Kafka核心介绍&#xff1a;​核心架构​核心特性​典型应用 2.Kafka对 ZooKeeper 的依赖&#xff1a;3.去 ZooKeeper 的演进之路&#xff1a;注&#xff1a;&#xff08;本文采用ZooKeeper3.8 Kafka2.8.1&#xff09; 二、Zookeeper1.核心架构与特性2.典型…...

RK3568驱动 SPI主/从 配置

一、SPI 控制器基础配置(先说主的配置&#xff0c;后面说从的配置) RK3568 集成高性能 SPI 控制器&#xff0c;支持主从双模式&#xff0c;最高传输速率 50MHz。设备树配置文件路径通常为K3568/rk356x_linux_release_v1.3.1_20221120/kernel/arch/arm64/boot/dts/rockchip。 …...

【全队项目】智能学术海报生成系统PosterGenius--风格个性化调整

​ &#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#x1f3c0;大模型实战训练营 ​&#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 1.前言 PosterGenius致力于开发一套依托DeepSeek…...

【系统移植】(六)第三方驱动移植

【系统移植】&#xff08;六&#xff09;第三方驱动移植 文章目录 【系统移植】&#xff08;六&#xff09;第三方驱动移植1.编译驱动进内核方法一&#xff1a;编译makefile方法二&#xff1a;编译kconfig方法三&#xff1a;编译成模块 2.字符设备框架 编译驱动进内核a. 选择驱…...

STM32实现一个简单电灯

新建工程的步骤 建立工程文件夹&#xff0c;Keil中新建工程&#xff0c;选择型号工程文件夹里建立Start、Library、User等文件夹&#xff0c;复制固件库里面的文件到工程文件夹工程里对应建立Start、Library、User等同名称的分组&#xff0c;然后将文件夹内的文件添加到工程分组…...

【shiro】shiro反序列化漏洞综合利用工具v2.2(下载、安装、使用)

1 工具下载 shiro反序列化漏洞综合利用工具v2.2下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1kvQEMrMP-PZ4K1eGwAP0_Q?pwdzbgp 提取码&#xff1a;zbgp其他工具下载&#xff1a; 除了该工具之外&#xff0c;github上还有其他大佬贡献的各种工具&#xff0c;有…...

vue进度条组件

<div class"global-mask" v-if"isProgress"><div class"contentBox"><div class"progresstitie">数据加载中请稍后</div><el-progress class"progressStyle" :color"customColor" tex…...