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 给子组件…...

Liunx-Ubuntu22.04.1系统下配置Anaconda+pycharm+pytorch-gpu环境配置
这里写自定义目录标题 Liunx-Ubuntu22.04.1系统下配置Anacondapycharmpytorch-gpu环境配置一、Anaconda3配置1.Anaconda安装2.Anaconda更新3.Anaconda删除 二、pycharm配置1.pycharm安装 三、pytorch配置 Liunx-Ubuntu22.04.1系统下配置Anacondapycharmpytorch-gpu环境配置 一…...

Postman之数据提取
Postman之数据提取 1. 提取请求头\request中的数据2. 提取响应消息\response中的数据3. 通过正在表达式提取4. 提取cookies数据 本文主要讲解利用pm对象对数据进行提取操作,虽然postman工具的页面上也提供了一部分的例子,但是实际使用时不是很全面&#…...

selenium元素定位校验以及遇到的元素操作问题记录
页面元素定位方法及校验 使用比较多的是通过id、class和xpath来对元素进行定位。在定位前可以现在浏览器验证是否可以找到指定的元素。这样就不用每添加一个元素定位都运行代码来检查定位方式表达式是否正确。 使用XPATH定位 在浏览器F12,找到元素,在元…...

在AndroidStudio中新建项目时遇到的Gradle下载慢问题,配置错的按我的来,镜像地址不知道哪个网页找的,最主要下载要快
android-studio-2024.2.1.11-windows Android 移动应用开发者工具 – Android 开发者 | Android Developers https://r4---sn-j5o76n7z.gvt1-cn.com/edgedl/android/studio/install/2024.2.1.11/android-studio-2024.2.1.11-windows.exe?cms_redirectyes&met1731775…...

用mv命令替换rm命令
# 用mv命令替换rm命令 主要内容来源自以上博文 rm命令穷凶极恶,以下为替换命令的方式,必做 步骤 修改vim ~/.bashrc加入以下代码 mkdir -p ~/.trash #在家目录下创建一个.trash文件夹(隐藏文件,ls -a 查看) alias rmdel #使用别名…...

电解车间铜业机器人剥片技术是现代铜冶炼过程中自动化和智能化的重要体现
电解车间铜业机器人剥片技术是现代铜冶炼过程中自动化和智能化的重要体现 电解车间铜业机器人剥片技术是现代铜冶炼过程中自动化和智能化的重要体现,它主要应用于铜电解精炼的最后阶段,即从阴极板上剥离出纯铜的过程。以下是该技术的几个关键点ÿ…...

【qt】控件2
1.frameGeometry和Geometry区别 frameGeometry是开始从红圈开始算,Geometry从黑圈算 程序证明:使用一个按键,当按键按下,qdebug打印各自左上角的坐标(相当于屏幕左上角),以及窗口大小 Widget::Widget(QWid…...

Frida反调试对抗系列(四)百度加固
本文只是交流技术,如有侵权请联系我删除。 知识星球:https://t.zsxq.com/kNlj4 前言: 上一篇文章我们提到 我们使用github开源魔改好的frida server 但是仍然有一些厂商的server不能通过,那么这篇文章针对百度加固 进行快速通…...

Redis 安全
Redis 安全 Redis是一个开源的,高性能的键值存储系统,它通常被用作数据库,缓存和消息代理。由于其高性能和简单的API,Redis在全球范围内被广泛使用。然而,与其他数据库系统一样,Redis的安全性也是至关重要…...

上交大与上海人工智能研究所联合推出医学多语言模型,模型数据代码开源
今天为大家介绍的是来自上海交通大学的王延峰与谢伟迪团队的一篇论文。开源的多语言医学语言模型的发展可以惠及来自不同地区、语言多样化的广泛受众。 来源丨 DrugAI、 机器人的脑电波 论文:https://www.nature.com/articles/s41467-024-52417-z MMedC࿱…...