1. 程式人生 > >郵件協議 (SMTP,POP3,HTTP)

郵件協議 (SMTP,POP3,HTTP)

SMTP協議通訊模型

  SMTP協議是TCP/IP協議族中的一員,主要對如何將電子郵件從傳送方地址傳送到接收方地址,也即是對傳輸的規則做了規定。SMTP協議的通訊模型並不複雜,主要工作集中在傳送SMTP和接收SMTP上:首先針對使用者發出的郵件請求,由傳送SMTP建立一條連線到接收SMTP的雙工通訊鏈路,這裡的接收SMTP是相對於傳送SMTP而言的,實際上它既可以是最終的接收者也可以是中間傳送者。傳送SMTP負責向接收SMTP傳送SMTP命令,而接收SMTP則負責接收並反饋應答。可大致用下面的通訊模型示意圖來表示:
SMTP協議的命令和應答

  從前面的通訊模型可以看出SMTP協議在傳送SMTP和接收SMTP之間的會話是靠傳送SMTP的 SMTP命令和接收SMTP反饋的應答來完成的。在通訊鏈路建立後,傳送SMTP傳送MAIL命令指令郵件傳送者,若接收SMTP此時可以接收郵件則作出OK的應答,然後傳送SMTP繼續發出RCPT命令以確認郵件是否收到,如果接收到就作出OK的應答,否則就發出拒絕接收應答,但這並不會對整個郵件操作造成影響。雙方如此反覆多次,直至郵件處理完畢。SMTP協議共包含10個SMTP命令,列表如下:

SMTP命令 命令說明
HELLO <domain> <CRLF> 識別傳送方到接收SMTP的一個HELLO命令
MAIL FROM:<reverse-path><CRLF> <reverse-path>為傳送者地址。此命令告訴接收方一個新郵件傳送的開始,並對所有的狀態和緩衝區進行初始化。此命令開始一個郵件傳輸處理,最終完成將郵件資料傳送到一個或多個郵箱中。
RCPT TO:<forward-path><CRLF> <forward-path>標識各個郵件接收者的地址
DATA <CRLF>
接收SMTP將把其後的行為看作郵件資料去處理,以<CRLF>.<CRLF>標識資料的結尾。
REST <CRLF> 退出/復位當前的郵件傳輸
NOOP <CRLF> 要求接收SMTP僅做OK應答。(用於測試)
QUIT <CRLF> 要求接收SMTP返回一個OK應答並關閉傳輸。
VRFY <string> <CRLF> 驗證指定的郵箱是否存在,由於安全因素,伺服器多禁止此命令。
EXPN <string> <CRLF> 驗證給定的郵箱列表是否存在,擴充郵箱列表,也常禁止使用。
HELP <CRLF> 查詢伺服器支援什麼命令

注:<CRLF>為回車、換行,ASCII碼分別為13、10(十進位制)。

SMTP協議的每一個命令都會返回一個應答碼,應答碼的每一個數字都是有特定含義的,如第一位數字為2時表示命令成功;為5表失敗;3表沒有完成。一些較複雜的郵件程式利用該特點,首先檢查應答碼的首數字,並根據其值來決定下一步的動作。下面將SMTP的應答碼列表如下:

應答碼 說明
501 引數格式錯誤
502 命令不可實現
503 錯誤的命令序列
504 命令引數不可實現
211 系統狀態或系統幫助響應
214 幫助資訊
220 <domain>服務就緒
221 <domain>服務關閉
421 <domain>服務未就緒,關閉傳輸通道
250 要求的郵件操作完成
251 使用者非本地,將轉發向<forward-path>
450 要求的郵件操作未完成,郵箱不可用
550 要求的郵件操作未完成,郵箱不可用
451 放棄要求的操作;處理過程中出錯
551 使用者非本地,請嘗試<forward-path>
452 系統儲存不足,要求的操作未執行
552 過量的儲存分配,要求的操作未執行
553 郵箱名不可用,要求的操作未執行
354 開始郵件輸入,以"."結束
554 操作失敗

SMTP協議的會話流程

  在進行程式設計之前有必要弄清SMTP協議的會話流程,其實前面介紹的內容已經可以大致勾勒出用SMTP傳送郵件的框架了,對於一次普通的郵件傳送,其過程大致為:先建立TCP連線,隨後客戶端發出HELLO命令以標識發件人自己的身份,並繼續由客戶端傳送MAIL命令,如伺服器應答為"OK",可繼續傳送RCPT命令來標識電子郵件的收件人,在這裡可以有多個RCPT行,而伺服器端則表示是否願意為收件人接受該郵件。在雙方協商結束後,用命令DATA將郵件傳送出去,其中對錶示結束的"."也一併傳送出去。隨後結束本次傳送過程,以QUIT命令退出。下面通過一個例項,從[email protected]傳送郵件到[email protected]來更詳細直觀地描述此會話流程:

R:220 sina.com Simple Mail Transfer Service Ready
S:HELLO sohu.com
R:250 sina.com
S:MAIL FROM:<[email protected]
R:250 OK
S:RCPT TO:<[email protected]
R:250 OK
S:DATA
R:354 Start mail input;end with "<CRLF>.<CRLF>"
S:……
R:250 OK
S:QUIT
R:221 sina.com Service closing transmission channel

郵件的格式化

  由於電子郵件結構上的特殊性,在傳輸時是不能當作簡單的文字來直接處理的,而必須按照一定的格式對郵件頭和郵件體進行格式化處理之後才可以被髮送。需要進行格式化的部分主要有:發件人地址、收件人地址、主題和傳送日期等。在RFC文件的RFC 822裡對郵件的格式化有詳盡的說明,有關詳情請參閱該文件。下面通過VC++6.0按照RFC 822文件規定將格式化郵件的部分編寫如下(部分程式碼):

//郵件頭準備
strTemp = _T( "From: " ) + m_strFrom; file://發件人地址
add_header_line( (LPCTSTR)strTemp );
strTemp = _T( "To: " ) + m_strTo; file://收件人地址
add_header_line( (LPCTSTR)strTemp );
m_tDateTime = m_tDateTime.GetCurrentTime();//傳送時間
strTemp = _T( "Data: " );
strTemp += m_tDateTime.Format( "%a, %d %b %y %H:%M:%S %Z" );
add_header_line( (LPCTSTR)strTemp );
strTemp = _T( "Subject: " ) + m_strSubject; file://主題
add_header_line( (LPCTSTR)strTemp );
file://郵件頭結束
m_strHeader += _T( ""r"n" );
file://郵件體準備
if( m_strBody.Right( 2 ) != _T( ""r"n" ) ) file://確認最後以回車換行結束
m_strBody += _T( ""r"n" );

其中add_header_line(LPCTSTR szHeaderLine)函式用於把szHeaderLine指向的字串追加到m_strHeader後面。其中,格式化後的郵件頭儲存在m_strHeader裡,格式化後的郵件體儲存在m_strBody中。

由Socket套接字為SMTP提供網路通訊基礎

許多網路程式都是採用Socket套接字實現的,對於一些標準的網路協議如HTTP、FTP和SMTP等協議的程式設計也是基於套接字程式的,只是埠號不再是隨意設定而要由協議來指定,比如HTTP埠在80、FTP是21,而SMTP則是25。Socket只是提供在指定的埠上同指定的伺服器從事網路上的通訊能力,至於客戶和伺服器之間是如何通訊的則由網路協議來規定,這對於套接字是完全透明的。因此可以使用Socket套接字為程式提供網路通訊的能力,而對於網路通訊連路建立好之後採取什麼樣的通訊應答則要按SMTP協議的規定去執行了。Socket套接字網路程式設計方面的文章資料非常豐富,限於本文篇幅,在此不再贅述,有關詳情請參閱相關文件。為簡便起見,沒有采用編寫較複雜的Windows Sockets API進行程式設計,而是使用經過較好封裝的MFC 的CSocket類。在正式使用套接字之前,也要先用AfxSocketInit()函式對套接字進行初始化,然後用Create()建立套接字物件,並由該套接字通過Connect()建立同郵件伺服器的連線。如果一切正常,再後續的工作中就是遵循SMTP協議的約定來使用Send()、Receive()函式來發送SMTP命令和接收郵件伺服器發來的應答碼以完成對郵件的傳送。

POP3協議命令原始碼及工作原理 
一 簡介: 
1. POP適用於C/S結構的離線模型的電子郵件協議,目前已發展到第三版,稱POP3。離線 模型即不能線上操作,不像IMAP4(netscape支援IMAP4) 
2. 當客戶機與伺服器連線並查詢新電子郵件時,被該客戶機指定的所有將被下載的郵件 都將被程式下載到客戶機,下載後,電子郵件客戶機就可以刪除或修改任意郵件,而無需與電子郵件伺服器進一步互動。 
3. POP3客戶向POP3伺服器傳送命令並等待響應,POP3命令採用命令列形式,用ASCII碼 表示。 伺服器響應是由一個單獨的命令列組成,或多個命令列組成,響應第一行以ASCII文字+ OK或-ERR指出相應 的操作狀態是成功還是失敗 
4. 在POP3協議中有三種狀態,認可狀態,處理狀態,和更新狀態。 當客戶機與伺服器建立聯絡時,一旦客戶機提供了自己身份併成功確認,即由認可狀態轉入處理狀態, 在完成相應的操作後客戶機發出quit命令,則進入更新狀態,更新之後最後重返認可狀態。如下圖 
等待連線 身份確認 quit命令 
—— |認可|————— |處理|——————|更新| 
|__________________________________| 
重返認可狀態 
5. 認可狀態的命令語句 
一般情況下,大多數現有的POP3客戶與伺服器執行採用ASCII明文傳送使用者名稱和口令,在 認可狀態等 待客戶連線的情況下,客戶發出連線,並由命令user/pass對在網路上傳送明文使用者名稱和 口令給伺服器 
進行身份確認。一旦確認成功,便轉入處理狀態。為了避免傳送明文口令的問題,有一種新的認證方法,命令為APOP,使用APOP,口令在 傳輸之前被加密。 當第一次與伺服器連線時,POP3伺服器向客戶機發送一個ASCII碼問候,這個問候由一串字元組成對每個客戶機是唯一的,與當時的時間有關,然後,客戶機把它的純文字口令附加到從伺服器接 收到的字串之後,然後計算出結果字串的MD5單出函式訊息摘要,客戶機把使用者名稱與MD5訊息摘要作為APOP命令的引數一起傳送出去。目前,大多數windows上的郵件客戶軟體不支援APOP命令,qpopper支援。 
6. POP3命令碼如下: 
命令 引數 狀態 描述 
------------------------------------------ 
USER username 認可 此命令與下面的pass命令若成功,將導致狀態轉換 
PASS password 認可 
APOP Name,Digest 認可 Digest是MD5訊息摘要 
------------------------------------------ 
STAT None 處理 請求伺服器發回關於郵箱的統計資料,如郵件總數和總位元組數 
UIDL [Msg#] 處理 返回郵件的唯一識別符號,POP3會話的每個識別符號都將是唯一的 
LIST [Msg#] 處理 返回郵件數量和每個郵件的大小 
RETR [Msg#] 處理 返回由引數標識的郵件的全部文字 
DELE [Msg#] 處理 伺服器將由引數標識的郵件標記為刪除,由quit命令執行 
RSET None 處理 伺服器將重置所有標記為刪除的郵件,用於撤消DELE命令 
TOP [Msg#] 處理 伺服器將返回由引數標識的郵件前n行內容,n必須是正整數 
NOOP None 處理 伺服器返回一個肯定的響應 
------------------------------------------ 
QUIT None 更新 
a.客戶機希望結束這次會話 
b.如果伺服器處於‘處理’狀態,那麼將進入‘更新’狀態以刪除任何標記為刪除的郵 
件 
c.導致由處理狀態到更新狀態,又重返認可狀態的轉變 
d.如果這個命令發出時伺服器處於‘認可’狀態,則結束會話,不進行‘更新’狀態。 

7. POP3協議在TCP/110埠上等待客戶連線請求。 
8. 若密碼為明文,我如何監聽? 
下面的命令在伺服器執行後在螢幕上顯示POP3連線及命令傳送的過程: 
#sniffit -a -A. -p 110 -b -s 192.169.11.12 
note: 192.168.11.12是客戶機IP地址 
你需要事先安裝sniffit這個埠監聽程式 
9. 考慮這種情況,若客戶在收取郵件時,假定為15封信等待接收,但由於線路問題,收 
到第10封時斷線了, 
為什麼下次收時仍然從第一封開始,也即為什麼前10封沒有被從伺服器上刪除掉? 
任何郵件的刪除都必須在quit命令發出後對已標記為刪除的郵件執行刪除操作,由於中 
途斷線,仍處於 
處理狀態,沒有機會執行quit命令以進行狀態轉換。 
10. pop3 session is locked by another session, please wait 10 minutes then t 
ry again. 
由於非正常操作引起POP3程式內部機制鎖住該次會話。 
11. Foxmail與OE(outlook express)的處理機制的不同。 
a. 假定伺服器上有三封郵件等待客戶機接收。用foxmail與OE的不同之處在於 
foxmail每收一封標記刪除一封,而OE則等全部接收完後再全部標記為刪除最後執行qui 
t命令。 
Foxmail OE 
------------------------------- 
retr 1 retr 1 
dele 1 retr 2 
retr 2 retr 3 
dele 2 dele 1 
retr 3 dele 2 
dele 3 dele 3 
quit quit 
b. Foxmail的遠端郵件管理是非常優秀的管理工具,假定伺服器上有三封信,對第一封 
,我們不想接收 
想從伺服器直接刪除;對第二封,想接收但不刪除,對第三封,這一次不想接收,分別 
標記後 
最後foxmail發出的命令是 
dele 1 
retr 2 
quit 
c. 若沒有foxmail,正好有幾封很大的信堵住了,我不想接收,想直接刪除它,或者想 
檢視這兩封是誰發的? 
直接在windows的DOS視窗下用命令列操作,如: 
# telnet my.isp.net 110 
user username 
pass password 
list

dele 3 
dele 5 
quit 
--



HTTP伺服器與HTTP客戶機之間的會話如下: 

1 客戶機與伺服器建立聯絡 

    與伺服器建立連線,就是與SOCKET建立連線,因此要指定機器名稱、資源名稱和斷口號,可以通過URL來提供這些資訊。URL的格式為: 
          HTTP://<IP地址>/[埠號]/[路徑][?<查詢資訊>] 
    資源的預設值是INDEX或DEFAULT,斷口號預設為80. 

2 客戶向伺服器提出請求 

    請求資訊包括希望返回的檔名和客戶機資訊.客戶機資訊以請求頭髮送給伺服器,請求頭包括HTTP方法和頭欄位. 
    HTTP方法常用的有GET、HEAD、POST,而PUT、DELETE、LINK、UNLINK方法 
許多HTTP伺服器都不使用。 

    頭欄位包括: 

          。DATE:請求傳送的日期和時間 

          。PARGMA:用於向伺服器傳輸與實現無關的資訊。這個欄位還用於告訴代理服 
務器,要從實際伺服器而不是從快取記憶體取資源 

          。FORWARDED:可以用來追蹤機器之間,而不是客戶機和伺服器的訊息。這個欄位 
可以用來追蹤在代理伺服器之間的傳遞路由。

      。MESSAGE_ID:用於唯一地標識訊息 

      。ACCEPT: 通知伺服器客戶所能接受的資料型別和尺寸。(*/*表示可以接受 
所有型別的資料。 

      。AOTHORIZATION:向伺服器提供旁路安全保護和加密機制,若伺服器 
不需要這個欄位,則不提供這個欄位 

          。FROM:當客戶應用程式希望想伺服器提供有關其電子郵件地址時使用 

          。IF-MODEFIED-SINCE用於提供條件GET。如果所請求的文件自從所指定 
的日期以來沒有發生變化,則伺服器應不傳送該物件。如果所傳送的日期格式不合 
法,或晚於伺服器的日期,伺服器會忽略該欄位 

          。BEFERRER:向伺服器進行資源請求用到的物件 

          。MIME-VERTION:用於處理不同型別檔案的MIME協議版本號 

          。USER-AGENT:有關發出請求的客戶資訊 

3 伺服器對請求作出應答 

    伺服器收到一個請求,就會立刻解釋請求中所用到的方法,並開始處理應答。 
伺服器的應答訊息也包含頭欄位形式的報文資訊。

    。報文第一行是狀態行,格式為 <HTTP版本號><狀態程式碼><解釋短語> 

         狀態碼是個三位數字碼,分為四類: 

             。以2開頭,表示請求被成功處理 
                 。以3開頭,表示請求被重定向 
              。以4開頭,表示客戶的請求有錯 
             。以5開頭,表示伺服器不能滿足請求 
         解釋短語是對狀態碼的解釋。 

    。報文還向客戶返回幾個頭欄位,如: 
         。STATUS CODE/REASON PHRASE 
             。DATE 
         。LAST-MODIFIED 
         。SERVER 
         。CONTENT _TYPE 
         。RETRY_AFTER:伺服器太忙時返回這個欄位 
         。WWW_AUTHENTICATE:當伺服器的安全機制要求客戶傳送某中授權資訊時使用該欄位 

    。報文最後是實體資訊,即客戶請求得到的HTTP伺服器上的資源內容。 

4 關閉客戶與伺服器之間的連線