Qt實(shí)現(xiàn)編寫SMTP客戶端的示例詳解
Qt編寫的SMTP客戶端(庫)
允許應(yīng)用程序通過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電子郵件(給多個收件人)。
(4) 電子郵件中的純文本和HTML(帶有內(nèi)聯(lián)文件)內(nèi)容。
(5) 多個附件和內(nèi)聯(lián)文件(用于HTML)。
(6) 不同的字符集(ascii,utf-8等)和編碼方法(7位,8位,base64)。
(7) 錯誤處理。
示例1 -一封簡單的(純文本)電子郵件
#include <QtGui/QApplication> #include "../src/SmtpMime" int main(int argc, char *argv[]) { QApplication a(argc, argv); // 這是Qt項(xiàng)目的Smtpclient的第一個演示應(yīng)用程序 // 首先,我們需要創(chuàng)建一個Smtpclient對象 // 我們將使用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)建一個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"); // 現(xiàn)在向電子郵件中添加一些文本。 // 首先,我們創(chuàng)建一個Mimetext對象。 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對象并設(shè)置用戶和密碼。 SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection); smtp.setUser("your_email_address@gmail.com"); smtp.setPassword("your_password"); // 創(chuàng)建一個 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)建附件對象 QFile image("image1.jpg"); MimeAttachment attachment(&image); // 可以設(shè)置文件類型。(默認(rèn)是application/octet-stream) attachment.setContentType("image/jpg"); // 現(xiàn)在將其添加到消息中 message.addPart(&attachment); // 添加另一個附件 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對象并設(shè)置用戶和密碼。 SmtpClient smtp("smtp.gmail.com", 465, SmtpClient::SslConnection); smtp.setUser("your_email@gmail.com"); smtp.setPassword("your_password"); // 創(chuàng)建一個 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)建一個MimeHtml對象。 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' />"); // 為每個圖像創(chuàng)建一個MimeInlineFile對象 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打開SMTPEmail.pro文件,編譯出SmtpMime2.dll動態(tài)庫,編譯動態(tài)庫的原因是因?yàn)槔锩娴膁emo運(yùn)行時需要鏈接這個動態(tài)庫。我編的是debug版的,你也可以編release的不過要放到對應(yīng)的文件夾下。
如下圖所示:
2. 再把步驟1中生成的SmtpMime2.dll文件拷貝到對應(yīng)的debug或release目錄下,我這里使用是debug的所以拷到debug文件下。
如下圖所示:
3. 雙擊打開demo2.pro文件,修改成SmtpMime2.dll庫的路徑即可。注意 : 路徑一定要對否則編譯會報(bào)錯
如下圖所示:
4.最后編譯+運(yùn)行,就可以成功地把demo2跑起來了,其他demo的使用方法應(yīng)該也類似。
如下圖所示:
5. 當(dāng)你點(diǎn)擊發(fā)送郵件的時候可能會報(bào)錯以下錯誤: qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed ,這個時候需要安裝OpenSSL。
6. 安裝OpenSSL
6.1 背景
Qt 使用QNetwork 模塊,進(jìn)行api請求時發(fā)生錯誤,錯誤信息:qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed
6.2 原因
當(dāng)你用HTTP形式的時候,會發(fā)現(xiàn)沒有問題,而使用HTTPS時,則會報(bào)錯。如果對計(jì)算機(jī)網(wǎng)絡(luò)有所了解的人,應(yīng)該大致懂原因。因?yàn)镠TTP 是超文本傳輸協(xié)議,信息是明文傳輸,存在安全風(fēng)險的問題。HTTPS 則解決 HTTP 不安全的缺陷,在 TCP 和 HTTP 網(wǎng)絡(luò)層之間加入了 SSL/TLS 安全協(xié)議,使得報(bào)文能夠加密傳輸。
SSL/TLS 協(xié)議建立的詳細(xì)流程:
Client
首先,由客戶端向服務(wù)器發(fā)起加密通信請求,也就是 Client的請求。在這一步,客戶端主要向服務(wù)器發(fā)送以下信息:
(1)客戶端支持的 SSL/TLS 協(xié)議版本,如 TLS 1.2 版本。
(2)客戶端生產(chǎn)的隨機(jī)數(shù)( Client Random ),后面用于生產(chǎn)「會話秘鑰」。
(3)客戶端支持的密碼套件列表,如 RSA 加密算法。
Sever
服務(wù)器收到客戶端請求后,向客戶端發(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)「會話秘鑰」。
(3)確認(rèn)的密碼套件列表,如 RSA 加密算法。
(4)服務(wù)器的數(shù)字證書。
客戶端回應(yīng)
客戶端收到服務(wù)器的回應(yīng)之后,首先通過瀏覽器或者操作系統(tǒng)中的 CA 公鑰,確認(rèn)服務(wù)器的數(shù)字證書的真實(shí)性。如果證書沒有問題,客戶端會從數(shù)字證書中取出服務(wù)器的公鑰,然后使用它加密報(bào)文,向服務(wù)器發(fā)送如下信息:
(1)一個隨機(jī)數(shù)( pre-master key )。該隨機(jī)數(shù)會被服務(wù)器公鑰加密。
(2)加密通信算法改變通知,表示隨后的信息都將用「會話秘鑰」加密通信。
(3)客戶端握手結(jié)束通知,表示客戶端的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個摘要,用來供服務(wù)端校驗(yàn)。
上面第一項(xiàng)的隨機(jī)數(shù)是整個握手階段的第三個隨機(jī)數(shù),這樣服務(wù)器和客戶端就同時有三個隨機(jī)數(shù),接著就用雙方協(xié)商的加密算法,各自生成本次通信的「會話秘鑰」。
服務(wù)器的最后回應(yīng)
服務(wù)器收到客戶端的第三個隨機(jī)數(shù)( pre-master key )之后,通過協(xié)商的加密算法,計(jì)算出本次通信的「會話秘鑰」。然后,向客戶端發(fā)生最后的信息:
(1)加密通信算法改變通知,表示隨后的信息都將用「會話秘鑰」加密通信。
(2)服務(wù)器握手結(jié)束通知,表示服務(wù)器的握手階段已經(jīng)結(jié)束。這一項(xiàng)同時把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個摘要,用來供客戶端校驗(yàn)。
至此,整個 SSL/TLS 的握手階段全部結(jié)束。接下來,客戶端與服務(wù)器進(jìn)入加密通信,就完全是使用普通的 HTTP 協(xié)議,只不過用「會話秘鑰」加密內(nèi)容。
回到qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed,那么這個報(bào)錯,就是缺少加密的手段,也就是沒有提供對HTTP加密的工具,從而無法進(jìn)行HTTPS通信。
6.3 解決方案
那么原因清楚了,就應(yīng)該知道,我們需要把加密這一步缺少的東西給補(bǔ)上。先說一點(diǎn),Qt本身是支持ssl的,但是因?yàn)榉稍??沒有自帶對應(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 接下來就去下載OpenSSL,下載方式有兩種:
(1). 下載OpenSSL源碼,自行編譯
https://www.openssl.org/source/
(2). 直接下載dll
Openssl下載網(wǎng)站: http://slproweb.com/products/Win32OpenSSL.html
我推薦第二個方法,省去自己編譯的時間。
然后在這個網(wǎng)站找到對應(yīng)的版本,比如我的版本是剛剛打印的1.1.1,同時,也要注意工程是32位還是64位,我這里選擇的是Win64 OpenSSL v1.1.1n Light,至于EXE還是MSI都沒差異。
6.5 安裝的時候,我選擇的是第二個,這樣可以方便的進(jìn)行dll文件的拷貝。
6.6 進(jìn)入OpenSSL安裝目錄。bin目錄下,拷貝這兩個dll文件到工程的exe程序所在路徑下。
6.7 比如我這里的路徑,直接放到exe根路徑下即可,然后程序即可正常運(yùn)行。
6.8 填寫好正確的郵箱和密碼再次點(diǎn)擊發(fā)送郵件,如果說提示"Authentification Failed"或者提示"Mail sending failed"可能是你填寫的郵箱和密碼錯了.
6.9 下面是郵件發(fā)送成功的截圖:
qq郵箱收到的郵件:
這里我也測試了一下,不同OpenSSL 版本的dll是否可以通用,發(fā)現(xiàn)高低版本都不行,必須得和Qt 打印出來的OpenSSL 版本一致才行。
另外,很多帖子都是說,把這兩個dll文件放到E:\QT\5.12.3\mingw73_64\bin路徑下,即Qt的安裝路徑下。這樣也可以,但是我覺得不好,個人認(rèn)為,盡量別去動Qt安裝路徑下的文件,保證不會被認(rèn)為破壞開發(fā)環(huán)境,或者以后排錯,不會導(dǎo)致當(dāng)時自己把文件放到了Qt安裝路徑下太久忘記,而在別的電腦上無法正常使用的情況。
以上就是Qt實(shí)現(xiàn)編寫SMTP客戶端的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Qt SMTP客戶端的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實(shí)現(xiàn)循環(huán)隊(duì)列
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)循環(huán)隊(duì)列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-01-01C++入門基礎(chǔ)之命名空間、輸入輸出和缺省參數(shù)
C++入門基礎(chǔ)篇的內(nèi)容為C++的基本特性,只有在掌握C++的基本特性后,是進(jìn)入后面類和對象學(xué)習(xí)的基礎(chǔ),下面這篇文章主要給大家介紹了關(guān)于C++入門基礎(chǔ)之命名空間、輸入輸出和缺省參數(shù)的相關(guān)資料,需要的朋友可以參考下2023-01-01C++ 中實(shí)現(xiàn)把EXCEL的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(ACCESS、MSSQL等)實(shí)例代碼
這篇文章主要介紹了C++ 中實(shí)現(xiàn)把EXCEL的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(ACCESS、MSSQL等)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04C++中的std::initializer_list使用解讀
這篇文章主要介紹了C++中的std::initializer_list使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07C語言編程中借助pthreads庫進(jìn)行多線程編程的示例
這篇文章主要介紹了C語言編程中借助pthreads庫進(jìn)行多線程編程的示例,文中的示例環(huán)境為Windows系統(tǒng),需要的朋友可以參考下2015-11-11