编写SMTP协议邮箱发信(QQ邮箱为例)
語(yǔ)言:C/C++ 編程軟件:VS2015 字符集:ASCII碼 主要使用:Winsock套接字庫(kù) 了解E-Mail協(xié)議:http://blog.csdn.net/aaron133/article/details/78365503 POP3郵箱收信(163郵箱為例):http://blog.csdn.net/aaron133/article/details/78413701
說(shuō)明:這里拿QQ郵箱做為例子,其他126、163郵箱也一樣可以,大同小異。
首先你要知道QQ提供的用于管理郵箱SMTP服務(wù)器監(jiān)聽(tīng)端口和服務(wù)器域名或IP地址。
一、如何查詢QQ的SMTP服務(wù)器:
QQ提供的SMTP服務(wù)器:http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=167
從上面可以知道:
SMTP服務(wù)器:smtp.qq.com
監(jiān)聽(tīng)端口號(hào):465、587.(使用25端口也行)
二、打開(kāi)QQ郵箱的SMTP/POP3服務(wù):
打開(kāi)QQ郵箱-->設(shè)置-->賬戶-->找到以下選項(xiàng):
POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服務(wù)
點(diǎn)擊開(kāi)啟“POP3/SMTP服務(wù)”選項(xiàng).
這時(shí),QQ會(huì)提供一個(gè)字符串密鑰(如:QKBE?KQQW UDVC BJAB),這個(gè)密鑰充當(dāng)著登錄密碼的作用.
如果你獲取密鑰后,修改了QQ密碼,那么密鑰會(huì)無(wú)效,那就要重新獲取.
如果你早已打開(kāi)了該SMTP服務(wù),但沒(méi)有密鑰,關(guān)閉重新打開(kāi)服務(wù).
得到密鑰:QKBE?KQQW UDVC BJAB ?實(shí)際使用密鑰時(shí):qkbekqqwudvcbjab
三、上傳QQ賬戶和密鑰時(shí),要轉(zhuǎn)換成“Base64編碼”再上傳給QQ的SMTP服務(wù)器:
(關(guān)于Base64編碼的概念:http://blog.csdn.net/aaron133/article/details/78352525)
使用以下的EncodeBase64函數(shù)即可將它們轉(zhuǎn)換成Base64編碼:
struct Base64Date6 ? ? { ? ?unsigned int d4 : 6; ? ? ?unsigned int d3 : 6; ? ?unsigned int d2 : 6; ? ?unsigned int d1 : 6; ? ? }; ? ? //轉(zhuǎn)換為Base64編碼函數(shù) void EncodeBase64(char *dbuf, char *buf128, int len) { //參數(shù)1:接收新Base64編碼的緩沖區(qū) 參數(shù)2:要轉(zhuǎn)換的字符串緩沖區(qū) 參數(shù)3:要轉(zhuǎn)換的字符串長(zhǎng)度struct Base64Date6 *ddd = NULL; int i = 0; char buf[256] = { 0 }; char *tmp = NULL; char cc = '\0'; memset(buf, 0, 256); strcpy_s(buf, 256, buf128); for (i = 1; i <= len / 3; i++) { tmp = buf + (i - 1) * 3; cc = tmp[2]; tmp[2] = tmp[0]; tmp[0] = cc; ddd = (struct Base64Date6 *)tmp; dbuf[(i - 1) * 4 + 0] = ConvertToBase64((unsigned int)ddd->d1); dbuf[(i - 1) * 4 + 1] = ConvertToBase64((unsigned int)ddd->d2); dbuf[(i - 1) * 4 + 2] = ConvertToBase64((unsigned int)ddd->d3); dbuf[(i - 1) * 4 + 3] = ConvertToBase64((unsigned int)ddd->d4); } if (len % 3 == 1) { tmp = buf + (i - 1) * 3; cc = tmp[2]; tmp[2] = tmp[0]; tmp[0] = cc; ddd = (struct Base64Date6 *)tmp; dbuf[(i - 1) * 4 + 0] = ConvertToBase64((unsigned int)ddd->d1); dbuf[(i - 1) * 4 + 1] = ConvertToBase64((unsigned int)ddd->d2); dbuf[(i - 1) * 4 + 2] = '='; dbuf[(i - 1) * 4 + 3] = '='; } if (len % 3 == 2) { tmp = buf + (i - 1) * 3; cc = tmp[2]; tmp[2] = tmp[0]; tmp[0] = cc; ddd = (struct Base64Date6 *)tmp; dbuf[(i - 1) * 4 + 0] = ConvertToBase64((unsigned int)ddd->d1); dbuf[(i - 1) * 4 + 1] = ConvertToBase64((unsigned int)ddd->d2); dbuf[(i - 1) * 4 + 2] = ConvertToBase64((unsigned int)ddd->d3); dbuf[(i - 1) * 4 + 3] = '='; } return; } //輔助計(jì)算的函數(shù) char ConvertToBase64(char uc) { if (uc < 26) { return 'A' + uc; } if (uc < 52) { return 'a' + (uc - 26); } if (uc < 62) { return '0' + (uc - 52); } if (uc == 62) { return '+'; } return '/'; }
四、開(kāi)始編寫(xiě):
通信步驟:
1、連接QQ的SMTP服務(wù)器 ?//2開(kāi)始,每一個(gè)命令都要接收一個(gè)服務(wù)器響應(yīng)碼
2、編輯郵件內(nèi)容和標(biāo)題格式:
? ?From:<發(fā)件人>\r\n(只是用于顯示)
? ?To:<收件人>\r\n(以;分割,只是用于顯示)
? ?Subject:主題\r\n\r\n"
? ?內(nèi)容
3、發(fā)送EHLO命令標(biāo)明身份
4、發(fā)送STARTTLS命令標(biāo)明使用安全傳輸層協(xié)議(TLS)
5、發(fā)送AUTH LOGIN命令使用郵箱登錄
6、上傳QQ賬號(hào)和郵箱密鑰(Base64編碼)
7、發(fā)送MAIL FROM命令指定發(fā)件人,就是當(dāng)前QQ郵箱.
8、發(fā)送RCPT TO命令指定一個(gè)或多個(gè)收件人
9、發(fā)送DATA命令準(zhǔn)備開(kāi)始發(fā)送郵件內(nèi)容
10、發(fā)送內(nèi)容,內(nèi)容以\r\n.\r\n結(jié)束
11、發(fā)送QUIT命令退出
頭文件:
#pragma once #include <Windows.h> #include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; #pragma comment(lib, "Ws2_32.lib")源文件:
#include "頭文件" //將要發(fā)送的郵件內(nèi)容 (這里的From和To只用于在郵件上顯示的) char EmailContents[] = "From:<3093575@qq.com>\r\n" //這里只是說(shuō)明發(fā)件人(QQ會(huì)檢查,標(biāo)記為垃圾郵件而發(fā)送失敗) "To:<109333576@qq.com>;<519194462@qq.com>\r\n" //這里只是說(shuō)明收件人(可以亂填的) "Subject:第一封測(cè)試郵件\r\n\r\n" //郵件標(biāo)題 "Hello World, Hello Email!"; //郵件內(nèi)容 //上面的EncodeBase64函數(shù)這里就不列出來(lái)了 //上面的ConvertToBase64函數(shù)這里就不列出來(lái)了 int main() { system("color 4e"); char buf[1500]; char login[128]; char pass[128]; ZeroMemory(buf, 1500); ZeroMemory(login, 128); ZeroMemory(pass, 128); //加載Winsock庫(kù) WSADATA WSAData; WSAStartup(MAKEWORD(2, 2), &WSAData); //創(chuàng)建TCP套接字 SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN dest; //端口25 dest.sin_port = htons(25);//587也可以 dest.sin_family = AF_INET; //獲取smtp.qq.com服務(wù)器域名所代表的IP地址 hostent* hptr = gethostbyname("smtp.qq.com"); memcpy(&dest.sin_addr.S_un.S_addr, hptr->h_addr_list[0], hptr->h_length); //連接騰訊的郵件SMTP服務(wù)器 int ok = connect(sockfd, (SOCKADDR*)&dest, sizeof(SOCKADDR)); if (ok != 0) exit(0); //用于向服務(wù)器標(biāo)明用戶身份,可以為發(fā)件人的服務(wù)器域名或者主機(jī)名 sprintf_s(buf, 1500, "EHLO Aaron-PC\r\n"); send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "QQ SMTP Server Say: \r\n" << buf << endl; //開(kāi)啟安全傳輸層協(xié)議(TLS) //這里的STARTTLS空格的后面要加一些字符才行,這里不是很懂,但這就可以了. sprintf_s(buf, 1500, "STARTTLS a\r\n"); send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "QQ SMTP Server Say: \r\n" << buf << endl; //郵箱認(rèn)證,發(fā)送該命令后依次發(fā)送郵箱賬號(hào)和密碼(賬號(hào)密碼均使用Base64編碼) sprintf_s(buf, 1500, "AUTH LOGIN\r\n"); send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Auth Login Receive:\r\n " << buf << endl; //發(fā)送QQ賬號(hào) ZeroMemory(buf, 1500); sprintf_s(buf, 1500, "3093575@qq.com");//你的郵箱賬號(hào) //將QQ賬號(hào)轉(zhuǎn)換為Base64編碼再發(fā)送 EncodeBase64(login, buf, strlen(buf)); sprintf_s(buf, 1500, "%s\r\n", login); send(sockfd, buf, strlen(buf), 0); cout << "Base64 UserName: " << buf << endl; //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "User Login Receive: \r\n" << buf << endl; //發(fā)送密鑰(相當(dāng)于QQ密碼,在上面的步驟得到) sprintf_s(buf, 1500, "qkbekqqwudvcbjab"); //將密鑰轉(zhuǎn)換為Base64編碼再發(fā)送 EncodeBase64(pass, buf, strlen(buf)); sprintf_s(buf, 1500, "%s\r\n", pass); send(sockfd, buf, strlen(buf), 0); cout << "Base64 Password:\r\n " << buf << endl; //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Send Password Receive:\r\n " << buf << endl; //發(fā)送MAIL FROM命令指定發(fā)件人 ZeroMemory(buf, 1500); sprintf_s(buf, 1500, "MAIL FROM: <3093575@qq.com>\r\n"); send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Set Mail From Receive:\r\n " << buf << endl; //發(fā)送一個(gè)或多個(gè)RCPT TO命令指定收件人 sprintf_s(buf, 1500, "RCPT TO:<%s>\r\n", "109333576@qq.com"); //109333576@qq.com為收件人1 send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Tell Sendto Receive: \r\n" << buf << endl; sprintf_s(buf, 1500, "RCPT TO:<%s>\r\n", "509333576@qq.com"); //509333576@qq.com為收件人2 send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Tell Sendto Receive: \r\n" << buf << endl; //發(fā)送一個(gè)DATA命令表示準(zhǔn)備開(kāi)始發(fā)送郵件內(nèi)容 sprintf_s(buf, 1500, "DATA\r\n"); send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Send Mail Prepare Receive: \r\n" << buf << endl; //發(fā)送郵件內(nèi)容,格式:以\r\n.\r\n作為結(jié)束標(biāo)記 sprintf_s(buf, 1500, "%s\r\n.\r\n", EmailContents); //郵件的內(nèi)容 send(sockfd, buf, strlen(buf), 0); //接收服務(wù)器響應(yīng) ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Send Mail Receive: \r\n" << buf << endl; //使用QUIT命令退出 sprintf_s(buf, 1500, "QUIT\r\n"); send(sockfd, buf, strlen(buf), 0); ZeroMemory(buf, 1500); recv(sockfd, buf, 1500, 0); cout << "Quit Receive: " << buf << endl; //清理工作 closesocket(sockfd); WSACleanup(); getchar(); return 0; }補(bǔ)充說(shuō)明:想要知道其中細(xì)節(jié)的問(wèn)題,需要學(xué)習(xí)SMTP協(xié)議,例如SMTP命令以及SMTP服務(wù)器三位數(shù)字響應(yīng)碼
缺點(diǎn):
1、360會(huì)報(bào)告有第三方軟件在使用郵箱發(fā)件.
2、明文傳輸賬號(hào)和密碼,有被人分析程序和抓包得到賬號(hào)密碼的風(fēng)險(xiǎn)。
與QQ的SMTP服務(wù)器交流過(guò)程:
測(cè)試發(fā)送結(jié)果:(我發(fā)送到了一個(gè)163郵箱、自己的QQ郵箱中)
總結(jié)
以上是生活随笔為你收集整理的编写SMTP协议邮箱发信(QQ邮箱为例)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Centos7安装Torque
- 下一篇: 您需要售后返修管理软件的N个理由