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

网站源码之家/百度网页版登录入口官网

网站源码之家,百度网页版登录入口官网,东营区住房和城乡建设局网站,公司是否可以做多个网站简单的TCP邮箱程序 **教学与实践目的:**学会网络邮件发送的程序设计技术。 1.SMTP协议 邮件传输协议包括 SMTP(简单邮件传输协议,RFC821)及其扩充协议 MIME; 邮件接收协议包括 POP3 和功能更强大的 IMAP 协议。 服务…

简单的TCP邮箱程序

**教学与实践目的:**学会网络邮件发送的程序设计技术。

1.SMTP协议

  • 邮件传输协议包括 SMTP(简单邮件传输协议,RFC821)及其扩充协议 MIME

  • 邮件接收协议包括 POP3 和功能更强大的 IMAP 协议。 服务邮件发送的服务器其端口为 25(如果开启 ssl 一般使用 465 端口,目前QQ邮箱已经强制必须使用加密连接方式,所以以下实验使用 465 端口), 服务邮件接收的服务器端口为 110(如果开启 SSL 一般使用 995 端口)。

SMTP协议解读以及如何使用SMTP协议发送电子邮件 - 一只会铲史的猫 - 博客园

  1. SMTP(简单邮件传输协议)

    • 命令有序性:SMTP的命令需要按照特定的顺序执行,以完成邮件的发送任务。每个命令都有其特定的作用,并且必须按照正确的顺序组合使用。
    • 请求应答模式:SMTP遵循请求应答式协议,客户端发送命令后,服务器会返回相应的响应。这种模式确保了命令的执行和结果的确认。
    • 响应格式:SMTP的响应格式通常包括一个三位数字的响应码,后面跟着响应描述,这与HTTP协议的响应格式相似。
  2. POP3(邮局协议第三版)

    • 命令独立性:POP3的命令如LIST、STAT、UIDL、TOP、RETR、DELE等,都可以独立使用,每个命令都有其特定的功能,如查看邮件列表、获取邮件内容等。
    • 数据流处理:在接收邮件时,POP3需要以流的方式处理数据,因为邮件数据可能不是一次性完整发送,而是分批次到达。
  3. Socket编程中的差异

    • 发送数据的简单性:在socket编程中,发送数据(如SMTP)相对简单,因为发送方可以按照自己的节奏发送数据,不需要考虑接收方的状态。
    • 接收数据的复杂性:接收数据(如POP3)需要判断数据是否完全接收,处理数据流的完整性,这比发送数据要复杂。
  4. 请求应答式协议

    • SMTP和HTTP:SMTP和HTTP都是请求应答式协议,客户端发送请求后,服务器返回响应。
    • HTTP的Keep-Alive:HTTP协议在设置为Keep-Alive时,可以进行多次请求和响应的交互,否则通常只有一次交互机会。

2.第三方邮箱设置

邮箱设置一定要开启 smtp/pop3 服务(以 QQ邮箱为例,在[邮箱设置]中 的[账户]中开启相关服务(获取授权码)

在这里插入图片描述

3.本机设置telnet支持

  • 首先打开电脑的控制面板
  • 点击程序–>启用或关闭windows程序
  • 启用Telnet客户端
  • 将telent这个服务勾选上然后点击确定。
  • 测试telent是否可用。打开cmd命令窗口,输入telnet

smtp.h

#ifndef SMTP_H
#define SMTP_H#include<QByteArray>
#include<QString>
#include<QTcpSocket>
class Smtp
{
public:Smtp(QByteArray username,QByteArray password);~Smtp();void SendData(QByteArray sendIp,QByteArray s_Title,QByteArray s_Content);QString WaitAndReadData();
private:QByteArray m_UserName="";QByteArray m_Password="";QTcpSocket * m_pSocket=nullptr;QString m_ReceiverData="";};#endif // SMTP_H

smtp.cpp

#include "smtp.h"
#include<QDebug>
Smtp::Smtp(QByteArray username,QByteArray password)
{if(username.contains("@163")){m_UserName= username;m_Password = password;}else{qDebug()<<"Error";}}
void Smtp::SendData(QByteArray sendIp, QByteArray s_Title, QByteArray s_Content)
{m_pSocket=new QTcpSocket();m_pSocket->connectToHost("smtp.163.com",25,QTcpSocket::ReadWrite);  //连接163邮箱m_pSocket->waitForConnected(1000);WaitAndReadData();m_pSocket->write("helo localhost\r\n");WaitAndReadData();m_pSocket->write("auth login\r\n");WaitAndReadData();m_pSocket->write(m_UserName.toBase64()+"\r\n");  //写入用户名WaitAndReadData();m_pSocket->write(m_Password.toBase64()+"\r\n");  //写入密码WaitAndReadData();m_pSocket->write("mail from: <"+m_UserName+">\r\n"); //发送的邮箱WaitAndReadData();m_pSocket->write("rcpt to: <"+sendIp+">\r\n"); //接收的邮箱WaitAndReadData();m_pSocket->write("data\r\n");  //开始写入WaitAndReadData();m_pSocket->write("from:<"+m_UserName+">\r\n");  //发送名称WaitAndReadData();m_pSocket->write("to:<"+sendIp+">");  //接受名称WaitAndReadData();m_pSocket->write("data\r\n");WaitAndReadData();m_pSocket->write("Subject:"+s_Title+"\r\n");  //标题m_pSocket->write("\r\n");m_pSocket->write(s_Content.append("\r\n")); //内容m_pSocket->write(".\r\n");WaitAndReadData();m_pSocket->write("quit\r\n");m_pSocket->disconnect();}QString Smtp::WaitAndReadData()
{m_pSocket->waitForReadyRead(1000);m_ReceiverData = m_pSocket->readAll();return m_ReceiverData;
}
Smtp::~Smtp()
{delete m_pSocket;
}

4.测试

Smtp smtp("邮箱名称","授权码"); //邮箱和密码都要用自己的  //注意是授权码,不是你登录邮箱的密码
smtp.SendData("aaa@qq.com","你好","这是一个测试程序");

安全的SSL邮箱程序

1. 使用QSslSocket设置参数

#include <QSslSocket>
#include <QSslCertificate>
#include <QSslKey>
#include <QTcpSocket>
#include <QHostAddress>
#include <QIODevice>
#include <QApplication>
#include <QDebug>class SSLClient : public QObject {Q_OBJECT
public:SSLClient(const QString &host, quint16 port, QObject *parent = nullptr) : QObject(parent) {QSslSocket *socket = new QSslSocket(QSsl::SslClientMode, this);connect(socket, &QSslSocket::encrypted, this, &SSLClient::onEncrypted);connect(socket, &QSslSocket::readyRead, this, &SSLClient::onReadyRead);connect(socket, &QSslSocket::sslErrors, this, &SSLClient::onSslErrors);connect(socket, &QSslSocket::connected, this, &SSLClient::onConnected);socket->connectToHostEncrypted(host, port);}private slots:void onConnected() {qDebug() << "Connected to the server";}void onEncrypted() {QSslSocket *socket = qobject_cast<QSslSocket *>(sender());if (socket) {QSslConfiguration config = socket->sslConfiguration();// 设置SSL/TLS协议config.setProtocol(QSsl::TlsV1_2);// 设置验证模式config.setPeerVerifyMode(QSslSocket::VerifyPeer);// 设置验证深度config.setPeerVerifyDepth(2);// 设置加密套件config.setCiphers(QSslConfiguration::supportedCiphers());// 加载本地证书和私钥(如果需要)// config.setLocalCertificate(QSslCertificate("path/to/certificate.pem", QSsl::Pem));// config.setPrivateKey(QSslKey("path/to/private_key.pem", QSsl::Rsa, QSsl::Pem));socket->setSslConfiguration(config);}}void onReadyRead() {QSslSocket *socket = qobject_cast<QSslSocket *>(sender());if (socket) {QByteArray data = socket->readAll();qDebug() << "Received:" << data;}}void onSslErrors(const QList<QSslError> &errors) {QSslSocket *socket = qobject_cast<QSslSocket *>(sender());if (socket) {foreach (const QSslError &error, errors) {qDebug() << "SSL Error:" << error.errorString();}socket->ignoreSslErrors();}}
};

2. 检查是否连接成功

在Qt中,使用QSslSocket类时,你可以通过几种方式来检查SSL连接是否已经成功建立:

  1. 使用信号
    QSslSocket提供了几个信号,可以用来确定连接的状态。

    • encrypted():当连接被加密时发出,表示SSL握手已经完成,并且数据传输现在是加密的。
    • connected():当底层的TCP连接建立时发出,但此时SSL握手可能还没有完成。
    • sslErrors():当SSL握手过程中出现错误时发出,你可以通过这个信号来检查是否有错误发生。
  2. 检查状态
    使用QSslSocketstate()方法可以获取当前的连接状态。QAbstractSocket::SocketState枚举值可以告诉你连接是否已经连接、正在连接、关闭等。

  3. 检查错误
    使用QSslSocketerror()方法可以获取最后一个错误。如果error()返回QAbstractSocket::NoError,则表示没有错误发生。

下面是一个简单的例子,展示了如何使用这些方法来检查SSL连接是否成功:

#include <QSslSocket>
#include <QDebug>// 假设你已经有了一个QSslSocket对象叫做sslSocket// 连接信号
connect(sslSocket, &QSslSocket::encrypted, this, []() {qDebug() << "SSL connection established";
});connect(sslSocket, &QSslSocket::sslErrors, this, [](QSslSocket *socket, const QList<QSslError> &errors) {foreach (const QSslError &error, errors) {qDebug() << "SSL error:" << error.errorString();}
});// 检查状态
if (sslSocket->state() == QAbstractSocket::ConnectedState) {if (sslSocket->error() == QAbstractSocket::NoError) {qDebug() << "SSL connection is up and running without errors";} else {qDebug() << "SSL connection has errors";}
}// 检查是否加密
if (sslSocket->isEncrypted()) {qDebug() << "The connection is encrypted";
} else {qDebug() << "The connection is not encrypted";
}

在这个例子中:

  • 我们连接了encrypted()信号,当SSL连接建立时,会在控制台输出消息。
  • 我们连接了sslErrors()信号,如果有SSL错误发生,会在控制台输出错误信息。
  • 我们使用state()方法检查当前的连接状态,并且使用error()方法检查是否有错误发生。
  • 我们使用isEncrypted()方法检查连接是否已经加密。

界面布局

layoutstretch

在Qt Creator中,layoutStretch是用于控制布局中各个元素(小部件或子布局)的拉伸系数(stretch factor)的属性。拉伸系数决定了元素在父布局中分配多余空间的比例。如果拉伸系数为0,则元素将保持其最小大小,而大于0的拉伸系数会让元素能够拉伸占据更多空间。

具体来说,layoutStretch属性可以在Qt Designer中直接设置,或者通过代码来调整。在Qt Designer中,当你选中一个布局器(比如水平布局器QHBoxLayout或垂直布局器QVBoxLayout),你可以在属性编辑栏中找到layoutStretch属性。这个属性允许你为布局中的每个元素设置一个拉伸系数,这些系数决定了在布局中的元素如何随着父容器大小的变化而变化。

例如,如果你有一个水平布局器中包含三个按钮,并且你将layoutStretch设置为1,2,3,那么当布局中的总空间需要分配时,第一个按钮将获得1份空间,第二个按钮获得2份空间,第三个按钮获得3份空间。这样,第三个按钮将占据比第一个和第二个按钮更多的空间。

在代码中,你可以使用QBoxLayoutsetStretchsetStretchFactor方法来设置拉伸系数。例如:

QHBoxLayout *layout = new QHBoxLayout;
QPushButton *button1 = new QPushButton("Button 1");
QPushButton *button2 = new QPushButton("Button 2");
QPushButton *button3 = new QPushButton("Button 3");layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);layout->setStretchFactor(button1, 1);
layout->setStretchFactor(button2, 2);
layout->setStretchFactor(button3, 3);

这段代码将创建一个水平布局,其中包含三个按钮,并且设置了不同的拉伸系数,从而影响它们在布局中的空间分配。

设置QSS

C++ Qt开发:PushButton按钮组件 - lyshark - 博客园

最终界面如图

在这里插入图片描述

QT中的多线程

在Qt中,有几种方式可以创建和使用多线程。以下是一些常见的方法:

使用QThread

最直接的方法是使用QThread类。你可以将需要在后台执行的任务移到一个新的线程中。以下是如何使用QThread的基本示例:

#include <QThread>
#include <QDebug>class Worker : public QObject {Q_OBJECT
public slots:void doWork() {qDebug() << "Work is being done in thread" << QThread::currentThreadId();// 执行一些耗时的操作}
};int main(int argc, char *argv[]) {QApplication app(argc, argv);QThread *thread = new QThread();Worker *worker = new Worker();worker->moveToThread(thread);// 当线程启动时,Worker对象的doWork()方法会被调用QObject::connect(thread, &QThread::started, worker, &Worker::doWork);// 当Worker对象的doWork()方法完成后,退出线程QObject::connect(worker, &Worker::finished, thread, &QThread::quit);// 确保线程结束时删除Worker对象QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater);// 同样,确保线程结束时删除线程对象本身QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);thread->start(); // 启动线程return app.exec(); // 进入Qt事件循环
}

使用QtConcurrent

Qt提供了QtConcurrent模块,它提供了一个高级的并发编程框架。你可以使用QtConcurrent::run()函数来简单地在后台线程中运行函数或成员函数。

#include <QtConcurrent>
#include <QDebug>
#include <QCoreApplication>void doWork() {qDebug() << "Work is being done in thread" << QThread::currentThreadId();// 执行一些耗时的操作
}int main(int argc, char *argv[]) {QCoreApplication app(argc, argv);// 这将在一个新的线程中异步执行doWork函数QtConcurrent::run(doWork);return app.exec(); // 进入Qt事件循环
}

使用QThreadPoolQRunnable

你可以创建一个QRunnable对象来表示一个任务,并将其提交给QThreadPool,这样它就可以在某个线程中异步执行。

#include <QThreadPool>
#include <QRunnable>
#include <QDebug>class WorkerRunnable : public QRunnable {
public:WorkerRunnable() {}void run() override {qDebug() << "Work is being done in thread" << QThread::currentThreadId();// 执行一些耗时的操作}
};int main(int argc, char *argv[]) {QCoreApplication app(argc, argv);WorkerRunnable *task = new WorkerRunnable();QThreadPool::globalInstance()->start(task); // 任务将被添加到全局线程池并执行return app.exec(); // 进入Qt事件循环
}

注意事项

  • 当使用多线程时,你需要确保对共享数据的访问是线程安全的。这通常意味着使用互斥锁(QMutex)或其他同步机制。
  • 避免在子线程中直接操作GUI,因为Qt的GUI工具包不是线程安全的。如果需要更新GUI,可以使用QObject::moveToThread()将对象移动到GUI线程,或者使用QMetaObject::invokeMethod()QSignalMapper来安全地从后台线程发出信号到GUI线程。
  • 使用QThread时,确保适当地管理线程的生命周期,避免内存泄漏。通常,当线程完成其任务后,你应该调用quit()来结束线程的事件循环,然后删除线程对象。

在多线程编程中,正确地管理资源和同步是至关重要的,以避免数据竞争、死锁和其他并发问题。

子线程更新GUI

Thread子类版

在Qt中,由于GUI组件不是线程安全的,你不能直接从子线程更新GUI。相反,你需要使用信号和槽机制来安全地从子线程发出信号,并在主线程(GUI线程)中接收这些信号并更新GUI。

以下是使用子线程更新GUI的步骤:

步骤 1: 创建一个继承自QObject的类,并在其中定义信号
// Worker.h
#ifndef WORKER_H
#define WORKER_H#include <QObject>class Worker : public QObject {Q_OBJECTpublic:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}signals:void updateGUI(const QString &data); // 自定义信号public slots:void doWork();
};#endif // WORKER_H
步骤 2: 实现Worker类的工作方法
// Worker.cpp
#include "Worker.h"void Worker::doWork() {// 执行一些耗时的操作QString result = "Done"; // 假设这是耗时操作的结果emit updateGUI(result); // 发送信号,而不是直接更新GUI
}
步骤 3: 在主线程中创建Worker对象,并将其移动到子线程
// main.cpp
#include <QApplication>
#include <QThread>
#include <QPushButton>
#include "Worker.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);QThread *thread = new QThread();Worker *worker = new Worker();worker->moveToThread(thread);// 当线程启动时,Worker对象的doWork()方法会被调用QObject::connect(thread, &QThread::started, worker, &Worker::doWork);// 从子线程接收信号,并在主线程中更新GUIQObject::connect(worker, &Worker::updateGUI, [](const QString &data) {qDebug() << "Update GUI in main thread:" << data;// 在这里更新GUI,例如设置文本到一个标签});// 当Worker对象的doWork()方法完成后,退出线程QObject::connect(worker, &Worker::updateGUI, thread, &QThread::quit);// 确保线程结束时删除Worker对象QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater);// 同样,确保线程结束时删除线程对象本身QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);// 创建一个简单的窗口来测试QPushButton button("Start Work");QObject::connect(&button, &QPushButton::clicked, thread, &QThread::start);button.show();return app.exec(); // 进入Qt事件循环
}

在这个例子中,我们创建了一个Worker对象,并将其移动到了一个新的QThread对象中。当线程启动时,Worker对象的doWork()方法会被调用。这个方法执行一些耗时的操作,并通过updateGUI信号发送结果。这个信号被连接到一个槽函数,该槽函数在主线程中更新GUI。

注意事项
  • 确保在子线程中不要直接操作GUI组件。
  • 使用QThread::quit()来优雅地退出线程。
  • 使用QObject::moveToThread()将对象移动到正确的线程。
  • 使用QObject::deleteLater()来确保线程和对象被适当地清理。

QConcurrent::run方法

在Qt中,尝试从非GUI线程直接更新GUI元素(如ui->textBrowser)会导致未定义行为,因为Qt的GUI组件并不是线程安全的。因此,你需要使用信号和槽来安全地从子线程更新GUI。

下面是如何正确使用QtConcurrent::run来更新GUI的步骤:

步骤 1: 定义一个信号

首先,在你的窗口类或任何适当的类中定义一个信号,用于传递数据到GUI线程。

// MyMainWindow.h
#include <QMainWindow>
#include <QString>namespace Ui {
class MyMainWindow;
}class MyMainWindow : public QMainWindow {Q_OBJECTpublic:explicit MyMainWindow(QWidget *parent = nullptr);~MyMainWindow();signals:void updateTextBrowser(const QString &text); // 定义一个信号private:Ui::MyMainWindow *ui;
};
步骤 2: 连接信号和槽

在你的窗口类的构造函数中,连接这个信号到一个槽,这个槽会更新GUI。

// MyMainWindow.cpp
#include "MyMainWindow.h"
#include "ui_MyMainWindow.h"MyMainWindow::MyMainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MyMainWindow),// 初始化ui组件等
{ui->setupUi(this);// 连接信号到槽connect(this, &MyMainWindow::updateTextBrowser, [this](const QString &text) {ui->textBrowser->append(text);});
}MyMainWindow::~MyMainWindow() {delete ui;
}
步骤 3: 使用QtConcurrent::run在后台线程中运行任务

现在,你可以使用QtConcurrent::run来在后台线程中运行你的任务,并通过信号将数据发送回GUI线程。

#include <QtConcurrent>
#include <QDebug>void startBackgroundTask(MyMainWindow *window) {QtConcurrent::run([=]() {while (true) {QString text = "nihao1";emit window->updateTextBrowser(text); // 发送信号QThread::sleep(1); // 避免过度占用CPU}});// 返回对象是QFuture<模板>,若lambda表达式没返回值,则是QFuture<void>型
}
步骤 4: 启动后台任务

在你的窗口类或其他适当的地方,调用startBackgroundTask函数来启动后台任务。

// 例如,在某个按钮的点击事件中
startBackgroundTask(this);
注意事项
  • 使用QThread::sleep或其他同步机制来控制循环的频率,以避免过度占用CPU。
  • 确保在适当的时候停止后台任务,例如在窗口关闭时。这可以通过设置一个控制变量来实现,当需要停止时,改变这个变量的值,并在循环中检查这个变量。
  • 在实际应用中,你可能需要更复杂的逻辑来确保线程安全地停止后台任务。

通过这种方式,你可以安全地从后台线程更新GUI,而不违反Qt的线程安全规则。

Socket跨线程调用的问题

Qt笔记-QTcpSocket跨线程调用(官方推荐方法,非百度烂大街方法)_setsocketdescriptor-CSDN博客

错误信息 "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread"

指出 QSocketNotifier 不能从另一个线程被启用或禁用。这是因为 QSocketNotifier 需要在创建它的线程中被注册和取消注册。当你尝试在不同的线程中操作 QSocketNotifier 时,就会遇到这个问题。

  1. 确保 QTcpSocketQSocketNotifier 在同一个线程中:如果你在子线程中使用 QTcpSocket,那么 QSocketNotifier 也应该在这个子线程中创建和管理。这意味着你需要避免跨线程调用 QObject 及其子类对象。

  2. 使用 moveToThread 方法:如果你有一个 QTcpSocket 对象,你可以使用 moveToThread 方法将其移动到新的线程中。对于 QSocketNotifier,在连接前确保它已经在正确的线程上。

  3. 避免在子线程中直接操作 QTcpSocket:如果你在子线程中直接操作 QTcpSocket,可能会导致 QSocketNotifier 相关的问题。你可以通过信号和槽机制,将数据从一个线程安全地传递到另一个线程。

  4. 在子线程中创建 QTcpSocket:如果你在子线程的构造函数或 run 函数中创建 QTcpSocket,那么所有的操作都应该在这个子线程中进行,以避免跨线程操作 QSocketNotifier

  5. 使用 Qt::QueuedConnection:在连接信号和槽时,使用 Qt::QueuedConnection 可以确保信号安全地传递给主线程中的槽函数,这样可以避免在子线程中直接操作 QSocketNotifier

  6. 避免跨线程调用 QObject:当你在主线程中创建 QObject 及其子类对象时,不要尝试在子线程中对其进行操作。相反,你应该在子线程中创建这些对象,以避免跨线程调用。

最终代码

main.cpp

#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}

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();bool CanReadLine();private:QSslSocket* ssl;bool isentrcyed = false;signals:
};#endif // TCPMAILCLIENT_H

tcpmailclient.hpp

#include "tcpmailclient.h"TCPMailClient::TCPMailClient(const QString &host, quint16 port, QObject *parent): QObject{parent} {QSslSocket *socket = new QSslSocket(this);this->ssl = socket;QObject::connect(ssl, &QSslSocket::encrypted, [=]() {this->isentrcyed = true;qDebug() << "连接成功";});QObject::connect(ssl, &QSslSocket::connected, this,[]() {qDebug() << "已连接到SMTP服务器";});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) {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();
}bool TCPMailClient::CanReadLine() { return ssl->canReadLine(); }

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include "tcpmailclient.h"
#include <QWidget>
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QCoreApplication>
#include <QFuture>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTsignals:void updateTextBrowser(const QString &text); // 定义一个更新响应内容的信号
public:Widget(QWidget *parent = nullptr);~Widget();void startReciveThread();void sendRequest();private slots:void on_pushButton_clicked();void on_pushButton_2_clicked();private:Ui::Widget *ui;TCPMailClient *mailclient;QFuture <void> future;bool recieveFlag = true;
};
#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);mailclient = new TCPMailClient("smtp.163.com", 465);// 连接信号到槽connect(this, &Widget::updateTextBrowser, [this](const QString &text) {// 通过触发信号回到ui线程更新ui->textBrowser->append(text);});startReciveThread();
}Widget::~Widget() {on_pushButton_2_clicked();delete ui;
}void Widget::startReciveThread() {future = QtConcurrent::run([=]() {recieveFlag = true;while (recieveFlag) {QString text = this->mailclient->recieve();if (text.isEmpty()){continue;}emit this->updateTextBrowser(text); // 发送信号QThread::msleep(500);               // 避免过度占用CPU}});
}void Widget::sendRequest() {QFuture<void> sendFuture = QtConcurrent::run([=]() {// 每发送一次会报错两行  QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread// 因为套接字是在主线程创建的,所以套接字的QSocketNotifier也要在主线程this->mailclient->send("HELO myfriend\r\n");QThread::msleep(500);this->mailclient->send("AUTH LOGIN\r\n");QThread::msleep(500);// 发送用户名QString username = "xxx@163.com";QString encodeUsrName  = username.toUtf8().toBase64();this->mailclient->send(QString("%1\r\n").arg(encodeUsrName));QThread::msleep(500);// 发送授权码QString password = "xxxxxxxxxxxX";QString encodepwd = password.toUtf8().toBase64();this->mailclient->send(QString("%1\r\n").arg(encodepwd));QThread::msleep(500);this->mailclient->send("MAIL FROM:<xxxx@163.com>\r\n");QThread::msleep(500);this->mailclient->send("RCPT TO:<xxxx@163.com>\r\n");QThread::msleep(500);this->mailclient->send("DATA\r\n");QThread::msleep(500);this->mailclient->send("FROM:xxxx@163.com\r\n");QThread::msleep(500);this->mailclient->send("SUBJECT: 测试邮件\r\n");QThread::msleep(500);this->mailclient->send("TO:xxxx@163.com\r\n");QThread::msleep(500);this->mailclient->send("\r\n");QThread::msleep(500);this->mailclient->send("这是一封测试邮件\r\n");QThread::msleep(500);this->mailclient->send(".\r\n");QThread::msleep(500);this->mailclient->send("QUIT\r\n");});// 如果需要,可以在这里使用sendFuture
}void Widget::on_pushButton_2_clicked() {recieveFlag = false;if (!future.isFinished()) {future.cancel();          // 请求取消任务future.waitForFinished(); // 等待任务完成}QApplication::quit();
}void Widget::on_pushButton_clicked() {sendRequest();
}

相关文章:

QT版发送邮件程序

简单的TCP邮箱程序 **教学与实践目的&#xff1a;**学会网络邮件发送的程序设计技术。 1.SMTP协议 邮件传输协议包括 SMTP&#xff08;简单邮件传输协议&#xff0c;RFC821&#xff09;及其扩充协议 MIME&#xff1b; 邮件接收协议包括 POP3 和功能更强大的 IMAP 协议。 服务…...

JavaSE:初识Java(学习笔记)

java是高级语言的面向对象语言 .[最贴近生活.最快速分析和设计程序] 一&#xff0c;计算机语言发展历史 二&#xff0c;Java体系结构 1&#xff0c;JavaSE&#xff08;Java Standard Edition&#xff09; 标准版&#xff0c;定位在个人计算机上的应用 这个版本是Jav…...

ClickHouse创建分布式表

ClickHouse创建分布式表 当数据量剧增的时候&#xff0c;clickhouse是采用分片的方式进行数据的存储的&#xff0c;类似于redis集群的实现方式。然后想进行统一的查询的时候&#xff0c;因为涉及到多个本地表&#xff0c;可以通过分布式表的方式来提供统一的入口。由于是涉及到…...

Flink转换算子

Apache Flink 是一个用于处理无界和有界数据的开源流处理框架。在 Flink 中&#xff0c;转换&#xff08;Transformation&#xff09;是数据流处理的核心组件之一&#xff0c;它们定义了如何从输入数据集生成输出数据集。以下是 Flink 中一些常见的转换算子&#xff1a; Map: 将…...

ThinkBook 14+ 2024 Ubuntu 触控板失效 驱动缺失问题解决

首先我的电脑是thinkbook14 2024&#xff0c;从ubuntu18到ubuntu24&#xff0c;笔者整个都试了一遍&#xff0c;触摸板都没反应&#xff0c;确认不是linux系统内核问题&#xff0c;原因为驱动缺失。 解决步骤&#xff1a; &#xff08;1&#xff09;下载驱动&#xff0c;网址如…...

【青牛科技】应用方案 | D75xx-150mA三端稳压器

概 述 D75XX系列是一套三端高电流低压稳压器。它们可以提供 150mA 的输出电流和允许输入电压高达30V。它们有几个固定的输出电压范围为3.0 V至5.0 V。CMOS 技术确保低电压降和低静态电流。 虽然这些设备主要设计为固定电压调节器&#xff0c;但它们可以与外部元件一起使用&…...

WPF之iconfont(字体图标)使用

1&#xff0c;前文&#xff1a; WPF的Xaml是与前端的Html有着高度相似性的标记语言&#xff0c;所以Xaml也可同Html一般轻松使用阿里提供的海量字体图标&#xff0c;从而有效的减少开发工作度。 2&#xff0c;下载字体图标&#xff1a; 登录阿里图标库网iconfont-阿里巴巴矢量…...

08、Java学习-面向对象中级:

Java学习第十二天——面向对象中级&#xff1a; IDEA&#xff1a; 创建完新项目后&#xff0c;再src里面创建.java文件进行编写。 src——存放源码文件&#xff08;.java文件&#xff09;&#xff1b;out——存放编译后的字节码文件&#xff08;.class文件&#xff09; 在I…...

springboot集成onlyoffice(部署+开发)

前言 最近有个项目需求是实现前端页面可以对word文档进行编辑&#xff0c;并且可以进行保存&#xff0c;于是一顿搜索&#xff0c;找到开源第三方onlyoffice&#xff0c;实际上onlyOffice有很多功能&#xff0c;例如文档转化、多人协同编辑文档、文档打印等&#xff0c;我们只用…...

LabVIEW编程基础教学(二)--数据类型

在LabVIEW中&#xff0c;数据类型是非常重要的基本概念&#xff0c;因为它们决定了如何存储和操作数据。掌握这些基础数据类型对于编写有效的程序非常关键。以下是LabVIEW中的基础数据类型介绍&#xff1a; 1. 数值类型&#xff08;Numeric&#xff09; 整型&#xff08;Inte…...

「Mac畅玩鸿蒙与硬件29」UI互动应用篇6 - 多选问卷小应用

本篇将带你实现一个多选问卷小应用&#xff0c;用户可以勾选选项并点击提交按钮查看选择的结果。通过本教程&#xff0c;你将学习如何使用 Checkbox 组件、动态渲染列表、状态管理及用户交互&#xff0c;构建完整的应用程序。 关键词 UI互动应用Checkbox 组件状态管理动态列表…...

Flutter中文字体设置指南:打造个性化的应用体验

在使用Flutter进行开发时&#xff0c;可能会遇到中文字体显示不正常或者字体不符合设计需求的情况。Flutter默认的中文字体往往无法满足某些用户对个性化和美观的需求。今天&#xff0c;我们就来详细探讨如何在Flutter应用中设置中文字体&#xff0c;并结合不同场景提供相应的解…...

git下载慢下载不了?Git国内国外下载地址镜像,git安装视频教程

git安装下载的视频教程在这 3分钟完成git下载和安装&#xff0c;git国内外下载地址镜像&#xff0c;Windows为例_哔哩哔哩_bilibili 一、Git安装包国内和国外下载地址镜像 1.1国外官方下载地址 打开Git的官方网站&#xff1a;Git官网下载页面。在页面上选择对应的系统&…...

安卓属性动画插值器(Interpolator)详解

属性动画&#xff08;Property Animation&#xff09;是 Android 中一个强大的动画框架&#xff0c;允许开发者对视图的任意属性&#xff08;如位置、透明度、尺寸、颜色等&#xff09;进行平滑的动态变化。插值器&#xff08;Interpolator&#xff09;作为属性动画的一部分&am…...

OSPF总结

1.定义及相关信息 (1)全称:Open ShortestPath First,开放式最短路径优先 (2)是一种基于链路状态算法的路由协议 (3)目前针对IPv4协议使用的是OSPF Version2(RFC2328) 目前针对IPv6 协议使用的是 OSPF Version3 ( RFC2740 ) (4)运行 OSPF 路由器之间…...

Spring Boot驱动的多维分类知识管理系统

1 绪论 1.1 研究背景 在这个推荐个性化的时代&#xff0c;采用新技术开发一个多维分类的知识管理系统来分享和展示内容是一个永恒不变的需求。本次设计的多维分类的知识管理系统有管理员和用户两个角色。 管理员可以管理用户信息&#xff0c;知识分类&#xff0c;知识信息等&am…...

CSS教程(七)- 背景

介绍 背景属性可以设置背景颜色、背景图片、背景平铺、背景图片位置、背景图像固定等。 1 背景颜色 属性名&#xff1a;background-color 作用&#xff1a;指定HTML元素的背景色。 取值&#xff1a;英文颜色、16进制、rgb、rgba、transparent&#xff08;一般为透明&#…...

PNG图片批量压缩exe工具+功能纯净+不改变原始尺寸

小编最近有一篇png图片要批量压缩&#xff0c;大小都在5MB之上&#xff0c;在网上找了半天要么就是有广告&#xff0c;要么就是有毒&#xff0c;要么就是功能复杂&#xff0c;整的我心烦意乱。 于是我自己用python写了一个纯净工具&#xff0c;只能压缩png图片&#xff0c;没任…...

【双十一特惠】腾讯云省钱攻略:如何智取云计算资源

前言 双十一不仅是购物的狂欢节&#xff0c;对于云计算用户来说&#xff0c;更是一个节省成本的绝佳时机。腾讯云&#xff0c;作为国内领先的云计算服务商&#xff0c;每年双十一都会推出一系列优惠活动。本文将为您揭开如何在这个购物节中&#xff0c;最大化利用腾讯云的优惠…...

爬虫学习8

Frida是一个动态代码插桩工具&#xff0c;允许开发者在运行时修改和调试应用程序 import ...&#xff1a;这行代码表示导入所需的模块或库&#xff0c;但具体的导入内容在图片中被省略了。 rdev frida.get_remote_device()&#xff1a;这行代码获取一个远程设备实例&#xff…...

双指针算法的妙用:提高代码效率的秘密(2)

双指针算法的妙用&#xff1a;提高代码效率的秘密&#xff08;2&#xff09; 前言&#xff1a; 小编在前几日讲述了有关双指针算法两道题目的讲解&#xff0c;今天小编继续进行有关双指针算法习题的讲解&#xff0c;老规矩&#xff0c;今天还是两道题目的讲解&#xff0c;希望…...

笔记--(网络3)、交换机、VLAN

交换机 交换机&#xff08;Switch&#xff09;意为“开关”是一种用于电&#xff08;光&#xff09;信号转发的网络设备。它可以为接入交换机的任意两个网络节点提供独享的电信号通路。最常见的交换机是以太网交换机。其他常见的还有电话语音交换机、光纤交换机等。 交换机的…...

昇思大模型平台打卡体验活动:基于MindSpore实现GPT1影评分类

如果你对MindSpore感兴趣&#xff0c;可以关注昇思MindSpore社区 大模型平台 平台说明 昇思大模型平台旨在为AI学习者和开发者提供在线学习的项目、模型、大模型体验和数据集的平台。我们也添加了各领域的经典数据集来帮助学习者解决AI学习过程中的一系列难题&#xff0c; 如…...

如何调整pdf的页面尺寸

用福昕阅读器打开pdf&#xff0c;进入打印页面&#xff0c;选择“属性”&#xff0c;在弹出的页面选择“高级” 选择你想调成的纸张尺寸&#xff0c;然后打印&#xff0c;打印出来的pdf就是调整尺寸后的pdf...

IDA*算法 Power Calculus————poj 3134

目录 闲聊 前言 DFS算法的无效搜索 BFS算法的空间浪费 IDDFS A*算法 IDA* Power Calculus 问题描述 输入 输出 问题分析 代码 闲聊 前几周在忙着数学竞赛&#xff0c;所以就没时间更新&#xff0c;高等数学&#xff0c;一生之敌&#xff0c;真不知道报名的时候我是怎么想…...

重磅!CoRL 2024顶刊会议 清华大学高阳研究组发布“基于大模型先验知识的强化学习”

正在德国举办的机器人研究领域的顶级学术会议CoRL 2024&#xff0c;清华大学交叉信息研究院高阳研究组发布重磅研究成果&#xff0c;提出“基于大模型先验知识的强化学习”框架&#xff08;Reinforcement Learning with Foundation Priors) 来促进具身智能体在操作任务中的学习…...

泷羽sec学习打卡-Windows基础命令

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 关于windows的那些事儿-Base 一、Windows-BaseWindows有哪些版本呢&#xff0c;有什么区别呢&#xff1f…...

RTC精度及校准

RTC精度偏差&#xff1a; RTC的基准时间和精度与石英晶体的频率相关&#xff0c;晶体的谐振频率取决于温度&#xff0c;因此RTC性能与温度相关&#xff0c;晶体的频率偏差是晶体正常频率的温度反转函数。 一、硬件方面&#xff1a; 1.使用高精度振荡器的RTC模块&#xff1b; …...

jQuery案例

以下是几个常见的 jQuery 示例&#xff0c;展示了它在不同场景下的应用&#xff1a; 1. 隐藏和显示元素 通过按钮点击隐藏和显示一个 <div> 元素。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><met…...

常见 HTTP 状态码分类和解释及服务端向前端返回响应时的最完整格式

目前开发的项目很大程度上是为明年的国产化做准备了&#xff0c;所以借这个机会把用了十年的自研系统全部重写&#xff0c;订立更严格的规范&#xff0c;本文记录一下返回格式及对应状态码。 常见 HTTP 状态码及解释 HTTP 状态码用于表示客户端请求的响应状态&#xff0c;它们…...