在以前接觸的項目中,一直都是在做網站時用到了發送mail 的功能,在asp 和.net 中都有相關的發送mail 的類, 實現起來非常簡單。最近這段時間因工作需要在C++ 中使用發送mail 的功能,上網搜了一大堆資料,終於得以實現,總結自己開發過程中碰到的一些問題,希望對需的人有所幫助, 由於能力有限, 文中不免有些誤解之處, 望大家能指正!!
其實,使用C++ 發送mail 也是很簡的事, 只需要瞭解一點SMTP 協議和socket 編程就OK 了, 網絡上也有很多高人寫好的mail 類源碼,有興趣的朋友可以下載看看.
1. SMTP 常用命令簡介
1). SMTP 常用命令
HELO/EHLO 向服務器標識用戶身份
MAIL 初始化郵件傳輸
mail from:
RCPT 標識單個的郵件接收人;常在MAIL 命令後面
可有多個rcpt to:
DATA 在單個或多個RCPT 命令後,表示所有的郵件接收人已標識,並初始化數據傳輸,以. 結束。
VRFY 用於驗證指定的用戶/ 郵箱是否存在;由於安全方面的原因,服務器常禁止此命令
EXPN 驗證給定的郵箱列表是否存在,擴充郵箱列表,也常被禁用
HELP 查詢服務器支持什麼命令
NOOP 無操作,服務器應響應OK
QUIT 結束會話
RSET 重置會話,當前傳輸被取消
如你對SMTP 命令不瞭解,可以用telnet 命令登陸到smtp 服務器用help 命令進行查看:
220 tdcsw.maintek.corpnet.asus ESMTP Sendmail 8.13.8/8.13.8; Sat, 9 Jan 2010 10:
45:09 +0800
help
214-2.0.0 This is sendmail
214-2.0.0 Topics:
214-2.0.0 HELO EHLO MAIL RCPT DATA
214-2.0.0 RSET NOOP QUIT HELP VRFY
214-2.0.0 EXPN VERB ETRN DSN AUTH
214-2.0.0 STARTTLS
214-2.0.0 For more info use "HELP <topic>".
214-2.0.0 To report bugs in the implementation see
214-2.0.0 http://www.sendmail.org/email-addresses.html
214-2.0.0 For local information send email to Postmaster at your site.
214 2.0.0 End of HELP info
2).SMTP 返回碼含義
* 郵件服務返回代碼含義
* 500 格式錯誤,命令不可識別(此錯誤也包括命令行過長)
* 501 參數格式錯誤
* 502 命令不可實現
* 503 錯誤的命令序列
* 504 命令參數不可實現
* 211 系統狀態或系統幫助響應
* 214 幫助信息
* 220 服務就緒
* 221 服務關閉傳輸信道
* 421 服務未就緒,關閉傳輸信道(當必須關閉時,此應答可以作爲對任何命令的響應)
* 250 要求的郵件操作完成
* 251 用戶非本地,將轉發向
* 450 要求的郵件操作未完成,郵箱不可用(例如,郵箱忙)
* 550 要求的郵件操作未完成,郵箱不可用(例如,郵箱未找到,或不可訪問)
* 451 放棄要求的操作;處理過程中出錯
* 551 用戶非本地,請嘗試
* 452 系統存儲不足,要求的操作未執行
* 552 過量的存儲分配,要求的操作未執行
* 553 郵箱名不可用,要求的操作未執行(例如郵箱格式錯誤)
* 354 開始郵件輸入,以. 結束
* 554 操作失敗
* 535 用戶驗證失敗
* 235 用戶驗證成功
* 334 等待用戶輸入驗證信息 for next connection>;
3) SMTP 命令應用
我們下需使用telnet 命令實現smtp 郵件的發送,具體操作如下:
220 tdcsw.com ESMTP Sendmail 8.13.8/8.13.8; Wed, 23 Dec 2009 18
:18:18 +0800
HELO tdcsw
250 tdcsw.com Hello x-128-101-1-240.ahc.umn.edu [128.101.1.240], pleased to meet you
MAIL FROM:[email protected]
250 2.1.0 [email protected]... Sender ok
RCPR TO:[email protected]
250 2.1.5 [email protected]... Recipient ok
DATA
354 Enter mail, end with "." on a line by itself
SUBJECT:HELLO
HI:
HAR are you?
.
250 2.0.0 nBNAIIG4000507 Message accepted for delivery
quit
221 2.0.0 tdcsw.maintek.corpnet.asus closing connection
Connection to host lost.
2. 用C++ 實現Mail 發送
爲了便於理解, 在此就不封裝Mail 類了, 而是以過程式函數方式給出.
1). 首先需要建立TCP 套接字, 連接端口依服務器而定,SMTP 服務默認端口爲25, 我們以 默認端口爲例
WSADATA wsaData;
int SockFD;
WSAStartup(MAKEWORD(2,2), &wsaData);
SockFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ServAddr.sin_family = AF_INET;
ServAddr.sin_addr.s_addr = inet_addr (“192.168.1.1”); //192.168.1.1 爲服務器地址
ServAddr.sin_port = htons(25);
connect(SockFD, (struct sockaddr *)&ServAddr, sizeof(ServAddr));
2). 發送SMTP 命令及數據
const char HEADER[] = "HELO smtpSrv/r/n"
"MAIL FROM: [email protected]/r/n"
"RCPT TO: [email protected]/r/n"
"DATA/r/n"
"FROM: [email protected]/r/n"
"TO: [email protected]/r/n"
"SUBJECT: this is a test/r/n"
"Date: Fri, 8 Jan 2010 16:12:30/r/n"
"X-Mailer: shadowstar's mailer/r/n"
"MIME-Version: 1.0/r/n"
"Content-type: text/plain/r/n/r/n";
//send HEADER
send(SockFD, HEADER, strlen(HEADER), 0);
const char CONTENT[]="this is content./r/n";
//send CONTENT
send(SockFD, CONTENT, strlen(CONTENT), 0);
send(SockFD, "./r/n", strlen("./r/n"), 0); //end
send(SockFD, "QUIT/r/n", strlen("QUIT/r/n"), 0); //quit
mail 發送的功能基本上就完成了, 當然, 如果是應用的話還是需要很多改動的地方的, 比如說添加附件等.
3). 附件功能
要使用SMTP 發送附件, 需要對SMTP 頭信息進行說明, 改變Content-type 及爲每一段正文添加BOUNDARY 名, 示例如下:
"DATA/r/n"
"FROM: [email protected]/r/n"
"TO: [email protected]/r/n"
"SUBJECT: this is a test/r/n"
"Date: Fri, 8 Jan 2010 16:12:30/r/n"
"X-Mailer: shadowstar's mailer/r/n"
"MIME-Version: 1.0/r/n"
"Content-type: multipart/mixed; boundary=/"#BOUNDARY#/"/r/n/r/n";
// 正文
"--#BOUNDARY#/r/n"
"Content-Type: text/plain; charset=gb2312/r/n"
"Content-Transfer-Encoding: quoted-printable/r/n"
郵件正文……….
// 附件
"/r/n--#BOUNDARY#/r/n"
"Content-Type: application/octet-stream; name=att.txt/r/n"
"Content-Disposition: attachment; filename=att.txt/r/n"
"Content-Transfer-Encoding: base64/r/n"
"/r/n"
附件正信息(base64 編碼)…..
Base64 編碼函數在網絡上很容易找到, 這裏就不給出源碼了, 如需要支持HTML 格式而又不知道如何寫這些頭信息, 可以用outlook 或foxmail 寫一封支持HTML 格式的mail, 查看其原文信息, 依照相同的格式發送就行了.
4). 實現抄送及密送
在SMTP 命令集中並沒有RCPT CC 或RCPT BCC 相關命令, 那要如何來實現抄送和密送功能呢?
在網絡上找到這樣一句話: “ 所有的接收者協商都通過RCPT TO 命令來實現,如果是BCC ,則協商發送後在對方接收時被刪掉信封接收者”, 開始一直不明白這句話是什麼意思? 後來通看查看foxmail 的郵件原文發現:
Date: Wed, 6 Jan 2010 12:11:48 +0800
From: "carven_li" < carven_li @smtp.com>
To: "carven" <[email protected]>
Cc: "sam" <[email protected]>,
"yoyo" <[email protected]>
BCC: "clara" <[email protected]>
Subject: t
X-mailer: Foxmail 5.0 [cn]
Mime-Version: 1.0
Content-Type: multipart/mixed;
boundary="=====001_Dragon237244850520_====="
才恍然大悟, 所謂的” 協商” 應該就是指發送方在Data 中指定哪些爲CC, 哪些爲BCC, 默認情況下什麼都不寫, 只發送第一個RCPT TO 的mail, 其他的都被過濾掉.