利用SMTP發送Mail詳解(一)

在以前接觸的項目中,一直都是在做網站時用到了發送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 格式而又不知道如何寫這些頭信息, 可以用outlookfoxmail 寫一封支持HTML 格式的mail, 查看其原文信息, 依照相同的格式發送就行了.

 

4). 實現抄送及密送

SMTP 命令集中並沒有RCPT CCRCPT 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 TOmail, 其他的都被過濾掉.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章