Qt邮箱程序改良版(信号和槽)
上一版代码可以正常使用,但是会报错
上一篇文章
错误信息 "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread" 指出了一个问题,即在非主线程中尝试启用或禁用套接字通知器(QSocketNotifier)。在Qt中,与GUI相关的操作,包括网络操作,通常需要在主线程中进行,因为Qt的GUI不是线程安全的。
在你的代码中,你使用了 QtConcurrent::run 来在后台线程中运行网络操作,这是不正确的,因为Qt的网络类(如QTcpSocket)不是线程安全的。尝试在非主线程中使用它们会导致这个错误。
要解决这个问题,你需要确保所有的网络操作都在主线程中进行。这里有几种方法可以解决这个问题:
-
不使用QtConcurrent::run:将网络操作放在主线程中,不使用QtConcurrent::run。这可能不是最佳解决方案,因为它会导致UI线程阻塞,用户体验不佳。
-
使用QThread和QObject::moveToThread:创建一个新的QThread,并将网络操作的QObject移动到这个新线程中。这样可以确保网络操作在新线程中运行,而不会干扰主线程。
-
使用Qt的网络线程:如果你使用的是QTcpSocket等网络类,可以考虑使用Qt自己的网络线程,例如通过QNetworkAccessManager来处理网络请求。
改进版本(信号与槽机制)
所以,为了改善代码,使用信号与槽机制对网络操作进行改善,通过在主线程触发信号,并传递参数到新线程workerThread的networkhandler对象进行数据处理,在触发信号返回主线程更新主线程的响应信息,达到自动化发送邮件的功能
效果展示

networkhandler.h
#ifndef NETWORKHANDLER_H
#define NETWORKHANDLER_H#include <QObject>class NetworkHandler : public QObject
{Q_OBJECT
public:explicit NetworkHandler(QObject *parent = nullptr);void handleData(const QString &data);signals:void updateTextBrowser(const QString &msg);
};#endif // NETWORKHANDLER_H
networkhandler.cpp
#include "networkhandler.h"NetworkHandler::NetworkHandler(QObject *parent): QObject{parent}
{}void NetworkHandler::handleData(const QString &data){if (data.isEmpty()){return;}emit this->updateTextBrowser(data);
}
tcpmailclient.h
#ifndef TCPMAILCLIENT_H
#define TCPMAILCLIENT_H#include <QObject>
#include <QObject>
#include <QtNetwork>
#include <QSslSocket>
#include <QSslCertificate>
#include <QSslKey>
#include <QTcpSocket>
#include <QHostAddress>
#include <QIODevice>
#include <QApplication>
#include <QDebug>class TCPMailClient : public QObject
{Q_OBJECT
public:explicit TCPMailClient(const QString &host, quint16 port,QObject *parent = nullptr);void send(QString msg);QString recieve();void close();private:QSslSocket* ssl;bool isentrcyed = false;signals:void Connected();void readyRead();
};#endif // TCPMAILCLIENT_H
tcpmailclient.cpp
#include "tcpmailclient.h"TCPMailClient::TCPMailClient(const QString &host, quint16 port, QObject *parent): QObject{parent} {this->ssl = new QSslSocket(this);QObject::connect(ssl, &QSslSocket::encrypted, [=]() {this->isentrcyed = true;qDebug() << "连接成功";});QObject::connect(ssl, &QSslSocket::connected, this,[this]() {qDebug() << "已连接到SMTP服务器";emit this->Connected();});// = 是复制要传递的变量, &是引用QObject::connect(ssl,&QSslSocket::readyRead,[&](){emit this->readyRead();});QObject::connect(ssl, &QSslSocket::errorOccurred, this,[](QAbstractSocket::SocketError socketError){qDebug() << "发生错误:" << socketError;});ssl->connectToHostEncrypted(host, port);// 可以连接信号,以确认数据已经发送connect(ssl, &QSslSocket::bytesWritten, this, [](qint64 bytes) {qDebug() << bytes << "bytes were written to the socket.";});
}void TCPMailClient::send(QString msg) {if (ssl->state() == QAbstractSocket::ConnectedState) {qDebug() << "发送" << msg;this->ssl->write(msg.toUtf8());} else {qDebug() << "SMTP连接未建立";}
}QString TCPMailClient::recieve() {// 等待并读取响应// if (ssl->waitForReadyRead()) {// QByteArray data = ssl->readAll(); // 读取所有可用数据// qDebug() << "响应内容:" << data;// return QString(data);// }// return QString();QByteArray data = ssl->readAll(); // 读取所有可用数据qDebug() << "响应内容:" << QString::fromUtf8(data);// return QString(data);return QString::fromUtf8(data); // 编码成字符串
}void TCPMailClient::close()
{ssl->disconnectFromHost();this->ssl->close();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include "networkhandler.h"
#include "tcpmailclient.h"
#include <QWidget>
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QCoreApplication>
#include <QFuture>
#include <QMessageBox>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTsignals:void dataReceived(const QString& data);public:Widget(QWidget *parent = nullptr);~Widget();void startWork();void dealWithResponse(const QString& response);private slots:void on_pushButton_clicked();void on_pushButton_2_clicked();private:Ui::Widget *ui;TCPMailClient *mailclient;QThread *workerThread;NetworkHandler * networkhandler;int sendState = 0;bool hassendUsername = false;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QFuture>
#include <QtConcurrent/QtConcurrent>Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->resize(600, 400);this->setWindowIcon(QIcon(":/icon/src/mail_icon.png"));
}Widget::~Widget() {on_pushButton_2_clicked();delete ui;
}void Widget::startWork()
{this->workerThread = new QThread;this->networkhandler = new NetworkHandler();this->networkhandler->moveToThread(workerThread);// 连接信号和槽connect(this, &Widget::dataReceived, networkhandler, &NetworkHandler::handleData);// connect(worker, &Worker::finished, workerThread, &QThread::quit);// connect(worker, &Worker::finished, worker, &Worker::deleteLater);connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);// 有新的数据可读时触发readyRead信号connect(mailclient,&TCPMailClient::readyRead,[this](){qDebug() << "触发readyRead信号";QString data = this->mailclient->recieve();emit this->dataReceived(data);});// 通过触发信号回到ui线程更新connect(networkhandler, &NetworkHandler::updateTextBrowser, this,[this](const QString &msg) {// 使用 Qt::QueuedConnection 确保在主线程中执行ui->textBrowser->append(msg);// 更新之后发送对应信息dealWithResponse(msg);}, Qt::QueuedConnection);// 启动线程workerThread->start();}
void Widget::dealWithResponse(const QString& response) {if (response.startsWith("220") && sendState == 0) {// 服务器已准备好接收命令this->mailclient->send("HELO xxx\r\n");sendState = 1; // 移动到下一个状态} else if (response.startsWith("250")) {// 如果服务器回复250,通常表示前一个命令成功执行switch (sendState) {case 1:this->mailclient->send("AUTH LOGIN\r\n");sendState = 2; // 准备发送用户名break;case 5:this->mailclient->send(QString("RCPT TO:<%1>\r\n").arg(ui->lineEdit_4->text()));sendState = 6; // 准备发送邮件数据break;case 6:// 服务器准备接收邮件数据this->mailclient->send("DATA\r\n");sendState = 7; // 邮件数据发送状态case 8:// 已发送退出QMessageBox::information(this,"提示信息","发送成功");break;default:qDebug() << "Unexpected 250 response in state" << sendState;break;}} else if (response.startsWith("334")) {// 服务器要求认证信息if (sendState == 2) {// 发送Base64编码的用户名QString username = QString::fromLatin1(QString("aaaa@163.com").toUtf8().toBase64());this->mailclient->send(username + "\r\n");qDebug() << username;sendState = 3; // 准备发送密码} else if (sendState == 3) {// 发送Base64编码的密码QString password = QString::fromLatin1(QString("MfjaiokgaaLN").toUtf8().toBase64());qDebug() << password;this->mailclient->send(password + "\r\n");sendState = 4; // 完成登录,准备发送MAIL FROM}} else if (response.startsWith("235")) {// 认证成功this->mailclient->send(QString("MAIL FROM:<%1>\r\n").arg(ui->lineEdit_3->text()));sendState = 5; // 准备发送RCPT TO} else if (response.startsWith("354") && sendState == 7) {// 发送邮件数据this->mailclient->send(QString("FROM:%1\r\n").arg(ui->lineEdit_3->text()));this->mailclient->send(QString("SUBJECT:%1\r\n").arg(ui->lineEdit_5->text()));this->mailclient->send(QString("TO:%1\r\n").arg(ui->lineEdit_4->text()));// 发送空行,隔开邮件正文和内容this->mailclient->send("\r\n");this->mailclient->send(ui->textEdit->toPlainText() + "\r\n");this->mailclient->send(".\r\n");this->mailclient->send("QUIT\r\n");// 退出状态sendState = 8;} else if (response.startsWith("5")) {// 5xx表示服务器端的错误qDebug() << "服务端报错" << response;} else {// 未识别的响应qDebug() << "Unrecognized SMTP response:" << response;}
}// 退出按钮
void Widget::on_pushButton_2_clicked() {QApplication::quit();
}// 发送按钮
void Widget::on_pushButton_clicked() {mailclient = new TCPMailClient("smtp.163.com", 465);// 连接之后触发槽函数处理接下来的步骤connect(mailclient,&TCPMailClient::Connected,this,&Widget::startWork);
}
相关文章:
Qt邮箱程序改良版(信号和槽)
上一版代码可以正常使用,但是会报错 上一篇文章 错误信息 "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread" 指出了一个问题,即在非主线程中尝试启用或禁用套接字通知器(QSocketNotifier)…...
入门到精通mysql数据(四)
5、运维篇 5.1、日志 5.1.1、错误日志 错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。 该日志是默认开启的,默认存放目录/var/log…...
Java 设计模式 详解
在Java开发中,设计模式是一种常见的、成熟的解决方案,用于应对特定的设计问题和复杂性管理。以下是一些常用的设计模式,它们可以分为三类:创建型模式、结构型模式和行为型模式。 一、创建型模式 创建型模式主要负责对象的创建&a…...
卡尔曼滤波学习资料汇总
卡尔曼滤波学习资料汇总 其实,当初的目的,是为了写 MPU6050 的代码的,然后不知不觉学了那么多,也是因为好奇、感兴趣吧 有些还没看完,之后笔记也会同步更新的 学习原始材料 【卡尔曼滤波器】1_递归算法_Recursive P…...
linux003.在ubuntu中安装cmake的方法
1.cmake安装程序下载 https://cmake.org/files/v3.30/ 2.解压并下载包 解压cmake压缩包 tar -xvzf cmake.tar.gz进入解压目录 cd cmake-<version>编辑~/.bashrc nano ~/.bashrc在文件的末尾添加如下代码 export PATH/home/xwl/software/cmake/bin:$PATH然后运行以…...
EtherNet/IP转Profinet网关连接发那科机器人配置实例解析
本案例主要展示了如何通过Ethernet/IP转Profinet网关实现西门子1200PLC与发那科搬运机器人的连接。所需的设备有西门子1200PLC、开疆智能Ethernet/IP转Profinet网关以及Fanuc机器人。 具体配置步骤:打开西门子博图配置软件,添加PLC。这是配置的第一步&am…...
自动化运维-检测Linux服务器CPU、内存、负载、IO读写、机房带宽和服务器类型等信息脚本
前言:以上脚本为今年8月1号发布的,当时是没有任何问题,但现在脚本里网络速度测试py文件获取不了了,测速这块功能目前无法实现,后面我会抽时间来研究,大家如果有建议也可以分享下。 脚本内容: #…...
ubuntu24.04设置开机自启动Eureka
ubuntu24.04设置开机自启动Eureka 之前我们是在/root/.bashrc的文件中增加了一条命令 nohup java -jar /usr/software/eurekaServer-auth-prd-03.jar > /usr/software/log.log 2>&1 &但上面这条命令只有在登录root的用户时,才会执行,如果…...
从视频帧生成点云数据、使用PointNet++模型提取特征,并将特征保存下来的完整实现。
文件地址 https://github.com/yanx27/Pointnet_Pointnet2_pytorch?spm5176.28103460.0.0.21a95d27ollfze Pointnet_Pointnet2_pytorch\log\classification\pointnet2_ssg_wo_normals文件夹改名为Pointnet_Pointnet2_pytorch\log\classification\pointnet2_cls_ssg "E:…...
工化企业内部能源能耗过大 落实能源管理
一、精准监测与数据分析 实时准确的数据采集 企业能耗管理系统能够对企业内各种能源(如电、水、气、热等)的使用情况进行实时监测。通过安装在能源供应线路和设备上的智能传感器,可以精确地采集能源消耗的各项数据,包括瞬时流量、…...
LSTM 和 LSTMCell
1. LSTM 和 LSTMCell 的简介 LSTM (Long Short-Term Memory): 一种特殊的 RNN(循环神经网络),用于解决普通 RNN 中 梯度消失 或 梯度爆炸 的问题。能够捕获 长期依赖关系,适合处理序列数据(如自然语言、时间序列等&…...
python成长技能之正则表达式
文章目录 一、认识正则表达式二、使用正则表达式匹配单一字符三、正则表达式之重复出现数量匹配四、使用正则表达式匹配字符集五、正则表达式之边界匹配六、正则表达式之组七、正则表达式之贪婪与非贪婪 一、认识正则表达式 什么是正则表达式 正则表达式(英语&…...
解决docker报Error response from daemon Get httpsregistry-1.docker.iov2错误
解决docker报Error response from daemon: Get "https://registry-1.docker.io/v2/"错误 报错详情 首先先看一下问题报错效果,我想要拉去nacos-serve:1.1.4的镜像,报如下错误,从报错信息可以看到,用于网络的愿意&…...
【论文分享】利用多源大数据衡量街道步行环境的老年友好性:以中国上海为例
本次给大家带来一篇SCI论文的全文翻译!该论文考虑了绿化程度、可步行性、安全性、形象性、封闭性和复杂性这六个指标,提出了一种基于多源地理空间大数据的新型定量评价模型,用于从老年人和专家的角度评估街道步行环境的老年友好程度ÿ…...
说说软件工程中的“协程”
在软件工程中,协程(coroutine)是一种程序运行的方式,可以理解成“协作的线程”或“协作的函数”。以下是对协程的详细解释: 一、协程的基本概念 定义:协程是一组序列化的子过程,用户能像指挥家…...
使用IDE实现java端远程调试功能
使用IDE实现java端远程调试功能 1. 整体描述2. 前期准备3. 具体操作3.1 修改启动命令3.2 IDE配置3.3 打断点3.4 运行Debug 4. 总结 1. 整体描述 在做项目时,有些时候,需要和第三方进行调式,但是第三方不在一起,需要进行远程调试&…...
javaScript交互案例2
1、京东侧边导航条 需求: 原先侧边栏是绝对定位当页面滚动到一定位置,侧边栏改为固定定位页面继续滚动,会让返回顶部显示出来 思路: 需要用到页面滚动事件scroll,因为是页面滚动,所以事件源是document滚动…...
JavaScript 浏览器对象模型 BOM
浏览器对象模型(Browser Object Model,BOM)是指一组与浏览器进行交互的 JavaScript 对象。它允许 JavaScript 与浏览器的组件进行交互,比如窗口、文档、历史记录等。BOM 不同于 DOM(文档对象模型)ÿ…...
基于MATLAB的激光雷达与相机联合标定原理及实现方法——以标定板为例
1.为什么要进行激光雷达和相机的联合标定? 激光雷达和相机的联合标定是为了将两种传感器的数据统一到同一坐标系中,从而实现更准确的环境感知。激光雷达提供精准的三维距离信息,而相机捕捉丰富的纹理和颜色,通过联合标定可以结合两…...
React(一)
文章目录 项目地址一、创建第一个react项目二、JSX语法2.1 生成列表2.2 大括号识别JS的表达式2.3 列表循环array2.4 条件判断以及假值显示2.5 复杂条件渲染2.6 事件处理2.7 添加CSS样式2.8 添加图片2.9 使用Fregments返回多个根标签2.10多条件渲染2.11 导出子组件2.12 给子组件…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
