Qt實(shí)現(xiàn)編寫(xiě)SMTP客戶端的示例詳解
Qt編寫(xiě)的SMTP客戶端(庫(kù))
允許應(yīng)用程序通過(guò)SMTP發(fā)送電子郵件(有文本,html,附件,內(nèi)聯(lián)文件等的MIME)。并支持SSL和SMTP身份驗(yàn)證。
Qt SMPT客戶端支持
(1) 與SMTP服務(wù)器的TCP和SSL連接。
(2) SMTP驗(yàn)證(PLAIN和LOGIN方法)。
(3) 發(fā)送MIME電子郵件(給多個(gè)收件人)。
(4) 電子郵件中的純文本和HTML(帶有內(nèi)聯(lián)文件)內(nèi)容。
(5) 多個(gè)附件和內(nèi)聯(lián)文件(用于HTML)。
(6) 不同的字符集(ascii,utf-8等)和編碼方法(7位,8位,base64)。
(7) 錯(cuò)誤處理。
示例1 -一封簡(jiǎn)單的(純文本)電子郵件
#include <QtGui/QApplication>
#include "../src/SmtpMime"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 這是Qt項(xiàng)目的Smtpclient的第一個(gè)演示應(yīng)用程序
// 首先,我們需要?jiǎng)?chuàng)建一個(gè)Smtpclient對(duì)象
// 我們將使用Gmail的smtp服務(wù)器(smtp.gmail.com,端口465,ssl)
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
// 我們需要設(shè)置用戶名(您的電子郵件地址)和密碼
// 為smtp認(rèn)證。
smtp.setUser("your_email_address@gmail.com");
smtp.setPassword("your_password");
// 現(xiàn)在我們創(chuàng)建一個(gè)MimeMessage對(duì)象。這是郵件。
MimeMessage message;
message.setSender(EmailAddress("your_email_address@gmail.com", "Your Name"));
message.addRecipient(EmailAddress("recipient@host.com", "Recipient's Name"));
message.setSubject("SmtpClient for Qt - Demo");
// 現(xiàn)在向電子郵件中添加一些文本。
// 首先,我們創(chuàng)建一個(gè)Mimetext對(duì)象。
MimeText text;
text.setText("Hi,\nThis is a simple email message.\n");
// 現(xiàn)在將其添加到郵件中
message.addPart(&text);
// 現(xiàn)在我們可以寄信了
smtp.connectToHost();
smtp.login();
smtp.sendMail(message);
smtp.quit();
}示例2 -發(fā)送附件
#include <QtGui/QApplication>
#include "../src/SmtpMime"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 首先創(chuàng)建Smtpclient對(duì)象并設(shè)置用戶和密碼。
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
smtp.setUser("your_email_address@gmail.com");
smtp.setPassword("your_password");
// 創(chuàng)建一個(gè) MimeMessage
MimeMessage message;
message.setSender(EmailAddress("your_email_address@gmail.com", "Your Name"));
message.addRecipient(EmailAddress("recipient@host.com", "Recipient's Name"));
message.setSubject("SmtpClient for Qt - Demo");
// 添加一些文本
MimeText text;
text.setText("Hi!\n This is an email with some attachments.");
message.addPart(&text);
// 現(xiàn)在我們創(chuàng)建附件對(duì)象
QFile image("image1.jpg");
MimeAttachment attachment(&image);
// 可以設(shè)置文件類型。(默認(rèn)是application/octet-stream)
attachment.setContentType("image/jpg");
// 現(xiàn)在將其添加到消息中
message.addPart(&attachment);
// 添加另一個(gè)附件
QFile document("document.pdf")
MimeAttachment documentAttachment(&document);
message.addPart(&documentAttachment);
// 現(xiàn)在我們可以寄信了
smtp.connectToHost();
smtp.login();
smtp.sendMail(message);
smtp.quit();
}示例3 -帶有嵌入圖像的HTML電子郵件(內(nèi)聯(lián)文件)
#include <QtGui/QApplication>
#include "../src/SmtpMime"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 首先創(chuàng)建Smtpclient對(duì)象并設(shè)置用戶和密碼。
SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection);
smtp.setUser("your_email@gmail.com");
smtp.setPassword("your_password");
// 創(chuàng)建一個(gè) MimeMessage
MimeMessage message;
message.setSender(EmailAddress("your_email_address@gmail.com", "Your Name"));
message.addRecipient(EmailAddress("recipient@host.com", "Recipient's Name"));
message.setSubject("SmtpClient for Qt - Example 3 - Html email with images");
// 現(xiàn)在我們需要為HTML內(nèi)容創(chuàng)建一個(gè)MimeHtml對(duì)象。
MimeHtml html;
html.setHtml("<h1> Hello! </h1>"
"<h2> This is the first image </h2>"
"<img src='cid:image1' />"
"<h2> This is the second image </h2>"
"<img src='cid:image2' />");
// 為每個(gè)圖像創(chuàng)建一個(gè)MimeInlineFile對(duì)象
QFile imageFile1("image1.jpg");
MimeInlineFile image1 (&imageFile1);
// 必須設(shè)置唯一的內(nèi)容id
image1.setContentId("image1");
image1.setContentType("image/jpg");
QFile imageFile2("image2.jpg");
MimeInlineFile image2 (&imageFile2);
image2.setContentId("image2");
image2.setContentType("image/jpg");
message.addPart(&html);
message.addPart(&image1);
message.addPart(&image2);
// 現(xiàn)在可以發(fā)送郵件了
smtp.connectToHost();
smtp.login();
smtp.sendMail(message);
smtp.quit();
}使用方法
1. 我這里下載的版本是SmtpClient-for-Qt v2.0的,首先使用Qt打開(kāi)SMTPEmail.pro文件,編譯出SmtpMime2.dll動(dòng)態(tài)庫(kù),編譯動(dòng)態(tài)庫(kù)的原因是因?yàn)槔锩娴膁emo運(yùn)行時(shí)需要鏈接這個(gè)動(dòng)態(tài)庫(kù)。我編的是debug版的,你也可以編release的不過(guò)要放到對(duì)應(yīng)的文件夾下。
如下圖所示:


2. 再把步驟1中生成的SmtpMime2.dll文件拷貝到對(duì)應(yīng)的debug或release目錄下,我這里使用是debug的所以拷到debug文件下。
如下圖所示:

3. 雙擊打開(kāi)demo2.pro文件,修改成SmtpMime2.dll庫(kù)的路徑即可。注意 : 路徑一定要對(duì)否則編譯會(huì)報(bào)錯(cuò)
如下圖所示:

4.最后編譯+運(yùn)行,就可以成功地把demo2跑起來(lái)了,其他demo的使用方法應(yīng)該也類似。
如下圖所示:

5. 當(dāng)你點(diǎn)擊發(fā)送郵件的時(shí)候可能會(huì)報(bào)錯(cuò)以下錯(cuò)誤: qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed ,這個(gè)時(shí)候需要安裝OpenSSL。

6. 安裝OpenSSL
6.1 背景
Qt 使用QNetwork 模塊,進(jìn)行api請(qǐng)求時(shí)發(fā)生錯(cuò)誤,錯(cuò)誤信息:qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed
6.2 原因
當(dāng)你用HTTP形式的時(shí)候,會(huì)發(fā)現(xiàn)沒(méi)有問(wèn)題,而使用HTTPS時(shí),則會(huì)報(bào)錯(cuò)。如果對(duì)計(jì)算機(jī)網(wǎng)絡(luò)有所了解的人,應(yīng)該大致懂原因。因?yàn)镠TTP 是超文本傳輸協(xié)議,信息是明文傳輸,存在安全風(fēng)險(xiǎn)的問(wèn)題。HTTPS 則解決 HTTP 不安全的缺陷,在 TCP 和 HTTP 網(wǎng)絡(luò)層之間加入了 SSL/TLS 安全協(xié)議,使得報(bào)文能夠加密傳輸。
SSL/TLS 協(xié)議建立的詳細(xì)流程:
Client
首先,由客戶端向服務(wù)器發(fā)起加密通信請(qǐng)求,也就是 Client的請(qǐng)求。在這一步,客戶端主要向服務(wù)器發(fā)送以下信息:
(1)客戶端支持的 SSL/TLS 協(xié)議版本,如 TLS 1.2 版本。
(2)客戶端生產(chǎn)的隨機(jī)數(shù)( Client Random ),后面用于生產(chǎn)「會(huì)話秘鑰」。
(3)客戶端支持的密碼套件列表,如 RSA 加密算法。
Sever
服務(wù)器收到客戶端請(qǐng)求后,向客戶端發(fā)出響應(yīng) 。服務(wù)器回應(yīng)的內(nèi)容有如下內(nèi)容:
(1)確認(rèn) SSL/ TLS 協(xié)議版本,如果瀏覽器不支持,則關(guān)閉加密通信。
(2)服務(wù)器生產(chǎn)的隨機(jī)數(shù)( Server Random ),后面用于生產(chǎn)「會(huì)話秘鑰」。
(3)確認(rèn)的密碼套件列表,如 RSA 加密算法。
(4)服務(wù)器的數(shù)字證書(shū)。
客戶端回應(yīng)
客戶端收到服務(wù)器的回應(yīng)之后,首先通過(guò)瀏覽器或者操作系統(tǒng)中的 CA 公鑰,確認(rèn)服務(wù)器的數(shù)字證書(shū)的真實(shí)性。如果證書(shū)沒(méi)有問(wèn)題,客戶端會(huì)從數(shù)字證書(shū)中取出服務(wù)器的公鑰,然后使用它加密報(bào)文,向服務(wù)器發(fā)送如下信息:
(1)一個(gè)隨機(jī)數(shù)( pre-master key )。該隨機(jī)數(shù)會(huì)被服務(wù)器公鑰加密。
(2)加密通信算法改變通知,表示隨后的信息都將用「會(huì)話秘鑰」加密通信。
(3)客戶端握手結(jié)束通知,表示客戶端的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時(shí)把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個(gè)摘要,用來(lái)供服務(wù)端校驗(yàn)。
上面第一項(xiàng)的隨機(jī)數(shù)是整個(gè)握手階段的第三個(gè)隨機(jī)數(shù),這樣服務(wù)器和客戶端就同時(shí)有三個(gè)隨機(jī)數(shù),接著就用雙方協(xié)商的加密算法,各自生成本次通信的「會(huì)話秘鑰」。
服務(wù)器的最后回應(yīng)
服務(wù)器收到客戶端的第三個(gè)隨機(jī)數(shù)( pre-master key )之后,通過(guò)協(xié)商的加密算法,計(jì)算出本次通信的「會(huì)話秘鑰」。然后,向客戶端發(fā)生最后的信息:
(1)加密通信算法改變通知,表示隨后的信息都將用「會(huì)話秘鑰」加密通信。
(2)服務(wù)器握手結(jié)束通知,表示服務(wù)器的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時(shí)把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個(gè)摘要,用來(lái)供客戶端校驗(yàn)。
至此,整個(gè) SSL/TLS 的握手階段全部結(jié)束。接下來(lái),客戶端與服務(wù)器進(jìn)入加密通信,就完全是使用普通的 HTTP 協(xié)議,只不過(guò)用「會(huì)話秘鑰」加密內(nèi)容。
回到qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed,那么這個(gè)報(bào)錯(cuò),就是缺少加密的手段,也就是沒(méi)有提供對(duì)HTTP加密的工具,從而無(wú)法進(jìn)行HTTPS通信。
6.3 解決方案
那么原因清楚了,就應(yīng)該知道,我們需要把加密這一步缺少的東西給補(bǔ)上。先說(shuō)一點(diǎn),Qt本身是支持ssl的,但是因?yàn)榉稍??沒(méi)有自帶對(duì)應(yīng)的dll。
我們先使用這段代碼打印當(dāng)前Qt支持的ssl版本:
#include <QSslSocket> #include <QDebug> qDebug()<< QSslSocket::sslLibraryBuildVersionString();
使用網(wǎng)絡(luò)需要加上Qt的網(wǎng)絡(luò)模塊,在pro文件里加上:
QT += network
我這里輸出的是
"OpenSSL 1.1.1d 10 Sep 2019"
6.4 接下來(lái)就去下載OpenSSL,下載方式有兩種:
(1). 下載OpenSSL源碼,自行編譯
https://www.openssl.org/source/
(2). 直接下載dll
Openssl下載網(wǎng)站: http://slproweb.com/products/Win32OpenSSL.html
我推薦第二個(gè)方法,省去自己編譯的時(shí)間。
然后在這個(gè)網(wǎng)站找到對(duì)應(yīng)的版本,比如我的版本是剛剛打印的1.1.1,同時(shí),也要注意工程是32位還是64位,我這里選擇的是Win64 OpenSSL v1.1.1n Light,至于EXE還是MSI都沒(méi)差異。

6.5 安裝的時(shí)候,我選擇的是第二個(gè),這樣可以方便的進(jìn)行dll文件的拷貝。

6.6 進(jìn)入OpenSSL安裝目錄。bin目錄下,拷貝這兩個(gè)dll文件到工程的exe程序所在路徑下。

6.7 比如我這里的路徑,直接放到exe根路徑下即可,然后程序即可正常運(yùn)行。

6.8 填寫(xiě)好正確的郵箱和密碼再次點(diǎn)擊發(fā)送郵件,如果說(shuō)提示"Authentification Failed"或者提示"Mail sending failed"可能是你填寫(xiě)的郵箱和密碼錯(cuò)了.

6.9 下面是郵件發(fā)送成功的截圖:

qq郵箱收到的郵件:

這里我也測(cè)試了一下,不同OpenSSL 版本的dll是否可以通用,發(fā)現(xiàn)高低版本都不行,必須得和Qt 打印出來(lái)的OpenSSL 版本一致才行。
另外,很多帖子都是說(shuō),把這兩個(gè)dll文件放到E:\QT\5.12.3\mingw73_64\bin路徑下,即Qt的安裝路徑下。這樣也可以,但是我覺(jué)得不好,個(gè)人認(rèn)為,盡量別去動(dòng)Qt安裝路徑下的文件,保證不會(huì)被認(rèn)為破壞開(kāi)發(fā)環(huán)境,或者以后排錯(cuò),不會(huì)導(dǎo)致當(dāng)時(shí)自己把文件放到了Qt安裝路徑下太久忘記,而在別的電腦上無(wú)法正常使用的情況。
以上就是Qt實(shí)現(xiàn)編寫(xiě)SMTP客戶端的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Qt SMTP客戶端的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于C++17實(shí)現(xiàn)的手寫(xiě)線程池
本文主要介紹了基于C++17實(shí)現(xiàn)的手寫(xiě)線程池,自己實(shí)現(xiàn)了Any類,Semaphore類以及Result類的開(kāi)發(fā),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
C語(yǔ)言結(jié)構(gòu)體(struct)的詳細(xì)講解
C語(yǔ)言中,結(jié)構(gòu)體類型屬于一種構(gòu)造類型(其他的構(gòu)造類型還有:數(shù)組類型,聯(lián)合類型),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言結(jié)構(gòu)體(struct)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03
LintCode-排序列表轉(zhuǎn)換為二分查找樹(shù)分析及實(shí)例
這篇文章主要介紹了LintCode-排序列表轉(zhuǎn)換為二分查找樹(shù)分析及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-04-04
6個(gè)變態(tài)的C語(yǔ)言Hello World程序
這篇文章主要介紹了6個(gè)變態(tài)的C語(yǔ)言Hello World程序,需要的朋友可以參考下2016-05-05
Cocos2d-x中背景音樂(lè)和音效使用實(shí)例
這篇文章主要介紹了Cocos2d-x中背景音樂(lè)和音效使用實(shí)例,注意本文中使用大量注釋來(lái)說(shuō)明背景音樂(lè)和音效的使用方法,需要的朋友可以參考下2014-09-09

