基於SMTP協議的E-MAIL電子郵件發送客戶端軟件C#實現

摘 要

電子郵件在當今社會中扮演了一個很重要的角色。越來越多的人在使用它。而且用它的人數勢必會繼續增加。雖然,現在已經有很多的郵件收發軟件例如著名的FoxMail 但是對於大多數的非專業的人來說它還是有點難度稍嫌負責。因此,我們就利用SMTP和Pop協議從底層開發了這個軟件。SMTP全稱是簡單郵件傳輸協議,它專門用來發送郵件用的。Pop全稱是郵局協議,是專門用於接收郵件的。我主要是負責如何實現發送郵件功能的。MailSend命名空間是我整個程序的核心。它包括兩個類。在SmtpMail的類中包含了一個SendMail的方法,它從底層詳細地實現了和服務器的交互操作。你既可以用它發送一個純文本郵件,也可以發送一個帶有附件的郵件,理所當然地,你也可以使用不同的SMTP服務器。經過測試,證實此軟件是一個支持多收信人,多附件的羣發軟件。雖然它沒有FoxMail那麼強大的功能,但是它容易掌握和使用。

關鍵詞:SMTP,命名空間,類,附件

Abstract

E-Mail play a veryimportant role in modern times.More and more people are using it,and the numberof it will larger and larger.Though there are a lot of software for sending andreceiving letters such as FoxMail which are also multifunctional,it isdifficult and complicated to the Most of people who are curbstone.For thisreason,we do this software with the rock-bottom protocol of SMTP and Pop. Thefull name of SMTP is Simple Mail Transfer Protocol.It is Used to sendingletters.The full name of Pop is Post Office Protocol which is Special to receiveletters.I basically take charge to how to realize the function of sendingletters. A namespace which is named MailSend is the soul of my programe.Itincludes two classes.A method named sendmial which realize the fuction step bystep belongs to the class of SmtpMail. It detailedly note the track of clientexchange to the server. You can use the software to send either a text E –Mailor a textE-mail with Attachments.You also can Send a letter to many addressee.In the nature of things,you can use a different SMTP service. The software Idid support multiletters and multisender after I test.It is simplier thanFoxMail and other professional softwares,but it is easy to hold and use.

Key Words: SMTP, nameSpace, Class, Attachment

1 引言

1.1 電子郵件介紹

電子郵件(簡稱E-mail)又稱電子信箱、電子郵政,它是一種用電子手段提供信息交換的通信方式。它是全球多種網絡上使用最普遍的一項服務。這種非交互式的通信,加速了信息的交流及數據傳送,它是一個簡易、快速的方法。通過連接全世界的Internet,實現各類信號的傳送、接收、存貯等處理,將郵件送到世界的各個角落。到目前爲止,可以說電子郵件是Internet資源使用最多的一種服務,E-mai1不只侷限於信件的傳遞,還可用來傳遞文件、聲音及圖形、圖像等不同類型的信息。

電子郵件不是一種“終端到終端”的服務,是被稱爲“存貯轉發式”服務。這正是電子信箱系統的核心,利用存貯轉發可進行非實時通信,屬異步通信方式。即信件發送者可隨時隨地發送郵件,不要求接收者同時在場,即使對方現在不在,仍可將郵件立刻送到對方的信箱內,且存儲在對方的電子郵箱中。接收者可在他認爲方便的時候讀取信件,不受時空限制。在這裏,“發送”郵件意味着將郵件放到收件人的信箱中,而“接收”郵件則意味着從自己的信箱中讀取信件,信箱實際上是由文件管理系統支持的一個實體。因爲電子郵件是通過郵件服務器(mai1server)來傳遞檔的。通常mailserver是執行多任務操作系統UNIX的計算機,它提供24小時的電子郵件服務,用戶只要向 mail server管理人員申請一個信箱賬號,就可使用這項快速的郵件服務。

電子郵件的工作原理:

  • 電子郵件系統是一種新型的信息系統,是通信技術和計算機技術結合的產物。電子郵件的傳輸是通過電子郵件簡單傳輸協議(Simple Mail Transfer Protocol,簡稱SMTP)這一系統軟件來完成的,它是Internet下的一種電子郵件通信協議。
  • 電子郵件的基本原理,是在通信網上設立“電子信箱系統”,它實際上是一個計算機系統。系統的硬件是一個高性能、大容量的計算機。硬盤作爲信箱的存儲介質,在硬盤上爲用戶分一定的存儲空間作爲用戶的“信箱”,每位用戶都有屬於自己的—個電子信箱。並確定—個用戶名和用戶可以自己隨意修改的口令。存儲空間包含存放所收信件、編輯信件以及信件存盤三部分空間,用戶使用口令開啓自己的信箱,並進行發信、讀信、編輯、轉發、存檔等各種操作。系統功能主要由軟件實現。
  • 電子郵件的通信是在信箱之間進行的。用戶首先開啓自己的信箱,然後通過鍵入命令的方式將需要發送的郵件發到對方的信箱中。郵件在信箱之間進行傳遞和交換,也可以與另—個郵件系統進行傳遞和交換。收方在取信時,使用特定賬號從信箱提取。

1.2 開發背景

當前流行的各大郵件客戶端軟件的除了最主要的收發信件之外,功能越來越複雜,但是人們平常真正用到的功能很少,很多功能尤其對於那些計算機知識相對缺乏的人來說,更加顯得太過於華麗而不太實用。有鑑於此,在瞭解RFC底層協議的基礎上,我們開發了這個各種功能相對簡單實用的郵件客戶端程序,簡化了很多不必要的功能。

1.3 開發環境及運行環境

1.3.1 開發環境

  • AMD Athlon(TM),512M內存,80G硬盤
  • Microsoft Windows XPProfessional
  • Microsoft VisualStudio 2003(C #)
  • Microsoft DeveloperNetwork for Visual Studio.NET 2003

1.3.2 運行環境

  • Intel® Pentium® 2及以上處理器,32M以上內存,4G以上硬盤
  • Microsoft® Windows™9X/NT操作系統
  • 800*600或以上的屏幕分辨率
  • 確保機器上安裝有.Net FrameWork 1.0或者以上版本

2 軟件架構及系統用例圖

2.1系統架構

軟件的總體架構如圖2.1:

2.2 系統總體用例

2.3 程序功能框圖

2.4 發送郵件類

是發送郵件的核心,類名爲SmtpMail,隸屬於命名空間MailSend。封裝了發送郵件的具體實現方法,也是具體的RFC用代碼實現的過程。而用戶通過具體的操作接口,接口與SmtpMail類通過交互操作來實現用戶發送信件的操作。

2.5 附加小功能類

是獲取一些諸如系統時間,當前用戶名,以及本機IP之類的類,類名爲AddExtra,隸屬於命名空間MailSend。

3 SMTP協議的研究

由於要開發的是郵件客戶端程序,就不得不用到SMTP協議和POP協議。而我個人負責的是郵件發送功能的實現,因此就必然會涉及到SMTP(Simple Mail Transfer Protocol)協議。SMTP被用來在因特網上發送郵件,該協議規定了一些基本的命令和方法使客戶端與服務器進行交互,以達到發送郵件的目的。

3.1 SMTP協議簡介及工作原理

3.1.1 介紹

簡單郵件傳輸協議(SMTP)的目標是可靠高效地傳送郵件,它獨立於傳送子系統而且僅要求一條可以保證傳送數據單元順序的通道。

SMTP的一個重要特點是它能夠在傳送中接力傳送郵件,傳送服務提供了進程間通信環境(IPCE),此環境可以包括一個網絡,幾個網絡或一個網絡的子網。理解到傳送系統(或IPCE)不是一對一的是很重要的。進程可能直接和其它進程通過已知的IPCE通信。郵件是一個應用程序或進程間通信。郵件可以通過連接在不同IPCE上的進程跨網絡進行郵件傳送。更特別的是,郵件可以通過不同網絡上的主機接力式傳送。

3.1.2 SMTP模型

SMTP設計基於以下通信模型:針對用戶的郵件請求,發送SMTP建立與接收SMTP之間建立一個雙向傳送通道。接收SMTP可以是最終接收者也可以是中間傳送者。SMTP命令由發送SMTP發出,由接收SMTP接收,而應答則反方面傳送。

一旦傳送通道建立,SMTP發送者發送MAIL命令指明郵件發送者。如果SMTP接收者可以接收郵件則返回OK應答。SMTP發送者再發出RCPT命令確認郵件是否接收到。如果SMTP接收者接收,則返回OK應答;如果不能接收到,則發出拒絕接收應答(但不中止整個郵件操作),雙方將如此重複多次。當接收者收到全部郵件後會接收到特別的序列,如果接收者成功處理了郵件,則返回OK應答。

SMTP提供傳送郵件的機制,如果接收方與發送方連接在同一個傳送服務下時,郵件可以直接由發送方主機傳送到接收方主機;或者,當兩者不在同一個傳送服務下時,通過中繼SMTP服務器傳送。爲了能夠對SMTP服務器提供中繼能力,它必須擁有最終目的主機地址和郵箱名稱。

MAIL命令參數是回覆路徑,它指定郵件從何處來;而RCPT命令的參數是轉發路徑的,它指定郵件向何處去。向前路徑是源路徑,而回復路徑是返回路徑(它用於發生錯誤時返回郵件)。

當同一個消息要發往不同的接收者時,SMTP遇到了向不同接收者發送同一份數據的複製品的問題,郵件命令和應答有一個比較奇怪的語法,應答也有一個數字代碼。在下面,例子中可以看到哪些使用實際的命令和應答。完整的命令和應答在第四節。

命令與應答對大小寫不敏感,也就是說,命令和應答可以是大寫,小寫或兩者的混合,但這一點對用戶郵件名稱卻不一定是對的,因爲有的主機對用戶名大小寫是敏感的。這樣SMTP實現中就將用戶郵箱名稱保留成初始時的樣子,主機名稱對大小寫不敏感。

命令與應答由ASCII字母表組成,當傳送服務提供8位字節傳送通道,每7位字符正確傳送,而最高位被填充爲0。當指定一般的命令或應答格式後,參數會由一些類似於語言的字符串表示出來,如”\<string\>“或”\<reverse-path\>“,這裏尖括號表示這是一種類似於語言的變量。

3.2 SMTP協議的命令和應答

3.2.1 SMTP協議的命令

SMTP命令定義了郵件傳輸或由用戶定義的系統功能。它的命令是由\<CRLF\>結束的字符串。而在帶有參數的情況下,命令本身由\<SP\>和參數分開,如果未帶參數可以直接和\<CRLF\>連接。郵箱的語法格式必須和接收站點的格式一致。下面討論SMTP命令和應答。

發送郵件操作涉及到不同的數據對象,它們由不同的參數相互連接。回覆路徑就是MAIL命令的參數,而轉發路徑則是RCPT命令的參數,郵件日期是DATA命令的參數。這些參數或者數據對象必須跟在命令後。這種模式也就要求有不同的緩衝區來存儲這些對象,也就是說,有一個回覆路徑緩衝區,一個轉發路徑緩衝區,一個郵件內容緩衝區。特定的命令產生自己的緩衝區,或使一個或多個緩衝的內容被清除。

HELLO (HELO)

此命令用於向接收SMTP確認發送SMTP。參數域包括髮送SMTP的主機名。接收SMTP通過連接確認命令來向發送SMTP確認接收SMTP。引命令和OK響應確認發送和接收SMTP進入了初始狀態,也就是說,沒有操作正在執行,所有狀態表和緩衝區已經被子清除。

MAIL (MAIL)

此命令用於開始將郵件發送到一個多個郵箱中。參數域包括回覆路徑。返回路徑中包括了可選的主機和發送者郵箱列表。當有主機列表時,它是一個回覆路徑源,它說明此郵箱是由在表中的主機一一傳遞發送(第一個主機是最後一個接收到此郵件的主機)過來的。此表也有作向發送者返回非傳遞信號的源路徑。因爲每個傳遞主機地址都被加在此表起始處,它就必須使用發送IPCE而不是接收IPCE(如果它們不是一個IPCE的話)清楚的名稱。一些出錯信息的回覆路徑可能就是空的。

此命令清除回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區,並且將此命令的回覆路徑信息插入到回覆路徑緩衝區中。

RECIPIENT (RCPT)

此命令用於確定郵件內容的唯一接收者;多個接收者將由多個此命令指定。轉發路徑中包括一個可選的主機和一個必須的目的郵箱。當出現主機列表時,這就是一個源路徑,它指明郵件必須向列表中的上一個主機發送。如果接收SMTP未實現郵件的傳遞發送,就會返回如未知本地用戶(550)的信息給用戶。

當郵件被傳遞發送時,傳遞主機必須將自己的名稱由轉發路徑的開始處移至回覆路徑的結束處。當郵件最終到達目的地時,接收SMTP將以它的主機郵件格式自己的名稱插入目標郵件中。例如,由傳遞主機A接收的帶有如下參數的郵件時:


 
  1. FROM:[email protected]
  2.   TO:<@HOSTA.ARPA,@HOSTB.ARPA:[email protected]>

將會變成如下形式:


 
  1.   FROM:<@HOSTA.ARPA:[email protected]>
  2.   TO:<@HOSTB.ARPA:[email protected]>.

此命令導致它的轉發路徑參數加入轉發路徑緩衝區中。

DATA (DATA)

接收者將跟在命令後的行作爲郵件內容。此命令導致此命令後的郵件內容加入郵件內容緩衝區。郵件內容可以包括所有128個ASCII碼字符。郵件內容由只包括一個句號的行結束,也就是如下的字符序列:”\<CRLF\>.\<CRLF\>“,它指示了郵件的結束。

郵件內容的結束指示要求接收者現在就處理保存的郵件內容。此過程將回復路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區的內容全部清空。如果操作成功,接收者必須返回OK應答;如果失敗也必須返回失敗應答。

當接收SMTP收到一條信息時,無論是用作轉發還是此郵件已經到達目的地,它都必須在郵件內容的開始處加上時間戳這一行,這一行指示了接收到郵件主機和發出此郵件主機的標識,以及接收到郵件內容的時間和日期。轉發的信件將有多行這樣的時間戳。當接收SMTP作最後一站的傳送時,它將返回路徑信息行插入郵件中。此行包括了發送命令中的\<reverse-path\>的信息。在這裏,最後一站的傳送的意思是郵件將被送到目的用戶手中,但在一些情況下,郵件可能需要更進一步的加工並由另外的郵件系統傳送。

可能在返回路徑中的郵箱與實際發送的郵件不一致,這個情況可能發生在需要傳送一個特定的錯誤處理信箱而不是信件發送者那裏。上面所述說明了,最後的郵件內容由一個返回路徑行,和在其後的一個或多個時間戳行構成。這些行後面是郵件內容的頭和體信息。

當處理後面的郵件數據指示部分成功時就需要特定的說明。這種情況可能發生在發送SMTP發現當郵件需要傳送給多個用戶時,只能夠成功地向其中的一部分發送信息這種情況下。在這種情況下,必須對DATA命令發送OK應答,而接收SMTP組織併發送一個”不可傳遞郵件”信息到信息的發送者。在此信息中或者發送一個不成功接收者的列表,或者每次發送一個不成接收者,而發送多次。所有不可傳遞郵件信息由MAIL命令發送。

返回路徑和接收時間戳例子:


 
  1. Return-Path: <@GHI.ARPA,@DEF.ARPA,@ABC.ARPA:[email protected]>
  2.   Received: from GHI.ARPA by JKL.ARPA ; 27 Oct 81 15:27:39 PST
  3.   Received: from DEF.ARPA by GHI.ARPA ; 27 Oct 81 15:15:13 PST
  4.   Received: from ABC.ARPA by DEF.ARPA ; 27 Oct 81 15:01:59 PST
  5.   Date: 27 Oct 81 15:01:01 PST
  6.   From: [email protected]
  7.   Subject: Improved Mailing System Installed
  8.   To: [email protected]
  9.   This is to inform you that ...

SEND(SEND)

此命令用於開始一個發送命令,將郵件發送到一個或多個終端上。參數域包括了一個回覆路徑,此命令如果成功就將郵件發送到終端上了。

回覆路徑包括一個可選的主機列表和發送者郵箱。當出現主機列表時,表示這是一個傳送路徑,郵件就是經過這個路徑上的每個主機發送到這裏的(列表上第一個主機是最後經手的主機)。此表用於返回非傳遞信號到發送者。因爲每個傳遞主機地址都被加在此表起始處,它就必須使用發送IPCE而不是接收IPCE(如果它們不是一個IPCE的話)清楚的名稱。一些出錯信息的回覆路徑可能就是空的。

此命令清除回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區,並且將此命令的回覆路徑信息插入到回覆路徑緩衝區中。

SEND OR MAIL (SOML)

此命令用於開始一個郵件操作將郵件內容傳送到一個或多個終端上,或者傳送到郵箱中。對於每個接收者,如果接收者終端打開,郵件內容將被傳送到接收者的終端上,否則就送到接收者的郵箱中。參數域包括回覆路徑,如果成功地將信息送到終端或郵箱中此命令成功。

回覆路徑包括一個可選的主機列表和發送者郵箱。當出現主機列表時,表示這是一個傳送路徑,郵件就是經過這個路徑上的每個主機發送到這裏的(列表上第一個主機是最後經手的主機)。此表用於返回非傳遞信號到發送者。因爲每個傳遞主機地址都被加在此表起始處,它就必須使用發送IPCE而不是接收IPCE(如果它們不是一個IPCE的話)清楚的名稱。一些出錯信息的回覆路徑可能就是空的。

此命令清除回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區,並且將此命令的回覆路徑信息插入到回覆路徑緩衝區中。

SEND AND MAIL (SAML)

此命令用於開始一個郵件操作將郵件內容傳送到一個或多個終端上,並傳送到郵箱中。如果接收者終端打開,郵件內容將被傳送到接收者的終端上和接收者的郵箱中。參數域包括回覆路徑,如果成功地將信息送到郵箱中此命令成功。

回覆路徑包括一個可選的主機列表和發送者郵箱。當出現主機列表時,表示這是一個傳送路徑,郵件就是經過這個路徑上的每個主機發送到這裏的(列表上第一個主機是最後經手的主機)。此表用於返回非傳遞信號到發送者。因爲每個傳遞主機地址都被加在此表起始處,它就必須使用發送IPCE而不是接收IPCE(如果它們不是一個IPCE的話)清楚的名稱。一些出錯信息的回覆路徑可能就是空的。

此命令清除回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區,並且將此命令的回覆路徑信息插入到回覆路徑緩衝區中。

RESET (RSET)

此命令指示當送郵件操作將被放棄。任何保存的發送者,接收者和郵件內容應該被拋棄,所有緩衝區和狀態表應該被清除,接收方必須返回OK應答。

VERIFY (VRFY)

此命令要求接收者確認參數是一個用戶。如果這是(已經知道的)用戶名,返回用戶的全名和指定的郵箱。此命令對回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區沒有影響。

EXPAND (EXPN)

此命令要求接收者確認參數指定了一個郵件發送列表,如果是一個郵件發送列表,就返回表中的成員。如果這是(已經知道的)用戶名,返回用戶的全名和指定的郵箱。此命令對回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區沒有影響。

HELP (HELP)

此命令導致接收者向HELP命令的發送者發出幫助信息。此命令可以帶參數,並返回特定的信息作爲應答。此命令對回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區沒有影響。

NOOP (NOOP)

此命令不影響任何參數和已經發出的命令。它只是說明沒有任何操作而不是說明接收者發送了一個OK應答。此命令對回覆路徑緩衝區,轉發路徑緩衝區和郵件內容緩衝區沒有影響。

QUIT (QUIT)

此命令指示接收方必鬚髮送OK應答然後關閉傳送信道。接收方在接到QUIT命令並做出響應之前不應該關閉通信信道。發送方在發送QUIT命令和接收到響應之前也不應該關閉信道。即使出錯,也不應該關閉信道。如果連接被提前關閉,接收方應該象接收到RSET命令一樣,取消所有等待的操作,但不恢復原先已經做過的操作。而發送方應該象接收到暫時錯誤(4XX)一樣假定命令和操作仍在支持之中。

TURN (TURN)

此命令指定接收方要麼發送OK應答並改變角色爲發送SMTP,要麼發送拒絕信息並保持自己的角色。如果程序A現在是發送SMTP,它發出TURN命令後接收到OK(250)應答,它就變成了接收SMTP。程序A就進入初始狀態,好象通信信道剛打開一樣,這時它發送220準備好服務信號。如果程序B現在是接收SMTP,它發出TURN命令後接收到OK(250)應答,它就變成了發送SMTP。程序A就進入初始狀態,好象通信信道剛打開一樣,這時它準備接收220準備好服務信號。

若要拒絕改變角色,接收方可以發送502應答。

對於這些命令的順序有一定的限制。對話的第一個命令必須是HELLO命令,此命令在此後的會話中也可以使用。如果HELLO命令的參數不可接受,必須由返回一個501失敗應答,同時接收到的SMTP必須保持在與剛纔一致的狀態下。 NOOP,HELP,EXPN和VRFY命令可以在會話的任何時候使用。MAIL,SEND,SOML或SAML命令開始一個郵件操作。一旦開始了以後就要發送RCPT和DATA命令。郵件操作可以由RSET命令終止。在一個會話中可以有一個或多個操作。

如果在操作開始參數不可接受,必須返回501失敗應答,同時接收到的SMTP必須保持在與剛纔一致的狀態下。如果操作中的命令順序出錯,必須返回503失敗應答,同時接收到的SMTP必須保持在與剛纔一致的狀態下。

會話的最後一個命令必須是QUIT命令。此命令在會話的其它時間不能使用。

COMMAND語法格式

命令是由命令碼和其後的參數域組成的。命令碼是四個字母組成的,不區別大小寫。因爲下面的命令的作用是相同的:


 
  1. MAIL Mail mail MaIl mAIl

這對於引導任何參數值的標記也是適用的,如TO和to就是一樣的。命令碼和參數由一個或多個空格分開。然而在回覆路徑和轉發路徑中的參數是區別大小寫的。特別是在一些主機上,”smith”和”Smith”就根本不是一個用戶。

參數域由不定長的字符串組成,它由\<CRLF\>結束,接收方在完全接收到此序列前不會採取任何行動。方括號代表可選的參數域。如果不選擇的話,系統選擇默認的設置。

下面是SMTP命令:


 
  1. HELO <SP> <domain> <CRLF>
  2. MAIL <SP> FROM:<reverse-path> <CRLF>
  3. RCPT <SP> TO:<forward-path> <CRLF>
  4. DATA <CRLF>
  5. RSET <CRLF>
  6. SEND <SP> FROM:<reverse-path> <CRLF>
  7. SOML <SP> FROM:<reverse-path> <CRLF>
  8. SAML <SP> FROM:<reverse-path> <CRLF>
  9. VRFY <SP> <string> <CRLF>
  10. EXPN <SP> <string> <CRLF>
  11. HELP [<SP> <string>] <CRLF>
  12. NOOP <CRLF>
  13. QUIT <CRLF>
  14. TURN <CRLF>

3.2.2 SMTP的應答碼

對SMTP命令的響應是多樣的,它確定了在郵件傳輸過程中請求和處理的同步,也保證了發送SMTP知道接收SMTP的狀態。每個命令必須有且只有一個響應。

SMTP響應由三位數字組成,其後跟一些文本。數字幫助決定下一個應該進入的狀態,而文本對人是有意義的。三位的響應已經包括了足夠的信息,不用再閱讀文本,文本可以直接拋棄或者傳遞給用戶。特別的是,文本是與接收和環境相關的,所以每次接收到的文本可能不同。在附錄E中可以看到全部的響應碼。正規的情況下,響應由下面序列構成:三位的數字,\<SP\>,一行文本和一個\<CRLF\>,或者也可以是一個多行響應。只有EXPN和HELP命令可以導致多行應答,然而,對所有命令,多行響應都是允許的。


 
  1. 500 格式錯誤,命令不可識別(此錯誤也包括命令行過長)
  2. 501 參數格式錯誤
  3. 502 命令不可實現
  4. 503 錯誤的命令序列
  5. 504 命令參數不可實現
  6. 211 系統狀態或系統幫助響應
  7. 214 幫助信息
  8. 220 <domain> 服務就緒
  9. 221 <domain> 服務關閉傳輸信道
  10. 421 <domain> 服務未就緒,關閉傳輸信道(當必須關閉時,此應答可以作爲對任何命令的響應)
  11. 250 要求的郵件操作完成
  12. 251 用戶非本地,將轉發向<forward-path>
  13. 450 要求的郵件操作未完成,郵箱不可用(例如,郵箱忙)
  14. 550 要求的郵件操作未完成,郵箱不可用(例如,郵箱未找到,或不可訪問)
  15. 451 放棄要求的操作;處理過程中出錯
  16. 551 用戶非本地,請嘗試<forward-path>
  17. 452 系統存儲不足,要求的操作未執行
  18. 552 過量的存儲分配,要求的操作未執行
  19. 553 郵箱名不可用,要求的操作未執行(例如郵箱格式錯誤)
  20. 354 開始郵件輸入,以<CRLF>.<CRLF>結束
  21. 554 操作失敗

4 RFC822

說道發送和接受郵件,我們就必須不得不提RFC822了。RFC822的全稱是“ARPA因特網文本信件格式的標準”(Standard for theFormat of ARPA Internet Text Messages)。該標準提供了郵件內容的格式和相關語義。

4.1 RFC822簡單介紹

RFC822規定的電子郵件內容全部由ASCII字符組成,就是通常所說的文本文件,因而標準將它稱爲Internet文本信件(Internet Text Messages)。

從直觀上看,信件非常簡單,就是一系列由ASCII字符組成的文本行,每一行以回車換行符(“CRLF“,就是ASCII碼的13和10)結束。

從組織上看,信件內容結構分爲兩大部分,中間用一個空白行(只有CRLF符的行)來分隔。第一部分稱爲信件的頭部(the header of the message),包括有關發送方、接收方、發送日期等信息。第二部分稱爲信件的體部(Body of the message),包括信件內容的正文文本。信頭是必需的,信體是可選的,即信體可有可無。如果不存在信體,用作分隔的空白行也就不需要。在信體中,也可以有用作分隔的空白行。這樣設計的信件便於進行語法分析,提取信件的基本信息。

在RFC822中規定,信件體就是一系列的向收信人表達信息的文本行,比較簡單,可以包含任意文本,並沒有附加的結構。信件頭則具有比較複雜的結構,在下一小節中詳述。

4.2 信件的頭部

4.2.1 信頭的一般格式

信頭的結構比較複雜,信頭由若干信頭字段(header field)組成,這些字段爲用戶和程序提供了關於信件的信息。要了解信頭的結構就要弄清楚各種信頭字段。

所有的信頭字段都具有相同的語法結構,從邏輯上說,包括四部分,字段名(field name),緊跟冒號”:” (colon),後跟字段體(field body),最後以回車換行符(CRLF)終止。即


 
  1. 信頭字段=字段名:字段體 CRLF

字段名必須由除了冒號和空格以外的可打印US-ASCII字符(其值在33和126之間)組成,大多數字段的字段名稱由一系列字母,數字組成,中間經常插入橫線符。字段名告訴電子郵件軟件如何翻譯該行中剩下的內容。

字段體可以包括除了CR和LF之外的任何ASCII字符。但是其中的空格,加括號的註釋,引號和多行字段都比較複雜,另外,字段體的語法和語義依賴於字段名,每個類型的字段有特定的格式。

RFC822爲信件定義了一些標準字段,並提供了用戶自行定義非標準字段的方法。

4.2.2 結構化字段和非結構化字段

每個字段所包含的信息不同,字段大體可以分爲結構化字段和非結構化字段。

結構化字段有特定的格式,由語法分析程序檢測。Sender 字段就是一個很好的例子,它的字段內容是信箱,有一個離散的結構。

非結構化的字段含有任意的數據,沒有固定格式。例如,Subject字段可以含有任意的文字,並且沒有固定格式。非結構化的字段數量較少,只有Subject、 Comments、擴展字段,非標準字段、IN-Reply和References等。所有其它字段都是結構化的。

4.2.3 信頭字段的元素

儘管Email信件的總體結構非常簡單,但一些信頭字段的結構是很複雜的。下面介紹一些大多數字段共有的元素。

1.空白符

像其它文本文件一樣,空白符包括空格符(ASCII碼32)和製表符Tab(ASCII碼19)。此外,行末的回車換行符CRLF也應算是空白符。使用空白符可以對字段進行格式化,增加它的可讀性。例如,每個字段間用CRLF來分離,在字段內用空格來分隔字段名和字段內容。在Subject後面的冒號和內容之間插入空格字符,會使字段結構更加清晰。在Email中,空白符的使用並沒有固定的規則,但應當正確地使用,僅在需要時才使用空白符,以便接收軟件進行語法分析。

2.註解

註解是由括號括起來的一系列字符,例如,(這份禮物)。註解一般用在非結構化的信頭字段中,沒有語法語義,僅爲人提供了一些附加的信息。如果在加引號的字符串中有包括在括號中的字符,那是字符串的一部分,不是註解。在解釋信件的時候,會將註解忽略,可以用一個空格字符代替它們,這樣就什麼也不會破壞。

3.字段摺疊

每個信頭字段從邏輯上說應當是一個由字段名、冒號、字段體和CRLF組成的單一的行,但爲了書寫與顯示的方便,增加可讀性,也爲了符合1000/80的行字符數的限制,可以將超過80個字符的信頭字段分爲多行,即對於比較長的字段,可以分割成幾行,形成摺疊。在結構化和非結構化字段中都允許摺疊。通過在字段中某些點插入CRLF符和至少一個或多個空白字符來實現字段的摺疊,第一行後面的行稱爲信頭字段的續行。續行都以一個空白符開始,這種方法稱爲摺疊(folding),例如標題字段Subject: This is a test可以表示爲:


 
  1. Subject: This is a test

反之,將一個被摺疊成多行的信頭字段恢復到它的單行表示的過程叫做去摺疊,只要簡單地移除後面跟着空格的CRLF,將摺疊空白符CRLF轉換成空格字符,就可以完成去摺疊(unfolding)。在分析被摺疊的字段的語法時,要把一個多行的摺疊字段展開爲一行,根據它的非摺疊的形式來分析它的語法與語義。

4.字段大小寫

字段名稱是不區分大小寫的,所以Subject、subject或SUBJECT都一樣。不過字段名稱大小寫有習慣的常用形式,如主題字段的大小寫形式通常爲Subject。字段體的大小寫稍微複雜點,要視情況而定。比如Subject後面的字段體,其中的大寫可能就是縮寫的專用名詞,不能改動。

4.2.4 標準的信頭字段

下面介紹RFC822中定義的常用的標準信頭字段。

與發信方有關的信頭字段  
格式:From:mailbox 舉例:From:[email protected] 寫信人字段。說明信件的原始創建者,給出他的電子信箱地址。創建者對信件的原始內容負責。
格式:Sender:mailbox 舉例:From:[email protected] Sender:[email protected] 發送者字段。說明實際提交發送這個信件的人,給出他的電子信箱地址。當發信人與寫信人不一樣時使用。比如,祕書替經理髮信。發送者對發送負責。
格式:Reply-TO:mailbox 舉例:From:[email protected] From:[email protected] 回覆字段。指定應當把回信發到哪裏。如果有此字段,回信將會發給它指定的郵箱,而不會發給From字段指定的郵箱。比如,發送的是經理的信,但回信應交辦公室處理。
與收信方有關的信頭字段  
格式:TO:mailbox list 舉例:TO:[email protected] 收信人字段。指定主要收信人的郵箱地址,可以是多個郵箱地址的列表,地址中間用逗號隔開。
格式:Cc:mailbox list 舉例:Cc:[email protected] 抄送字段。指定此信件要同時發給哪些人,也稱爲抄送。也可以使用郵箱地址列表,抄送給多個人。
格式:Bcc:mailbox list 密抄字段。指定此信件要同時祕密發給哪些人,也稱爲密件抄送。也可以使用郵箱地址列表,密抄給多個人。
其它的信頭字段  
格式:Date:date-time 舉例:Date:Tue,04 Dec 2004 16:18:08 +800 日期字段:Date字段含有電子郵件創建的日期和時間。
格式:Subject:*text 舉例:Subject:Hello! Subject:Re:Hello! 信件主題字段。描述信件的主題。當回覆信件時,通常在主題前面增加“Re:”前綴,標記爲該信件爲回覆信件:當信件被轉發時,通常在主題文字前面加上“Fw:”,“Fwd:”這樣的前綴。
格式:Received: [“from” domain] ;發送主機 [“by” domain] ;接收主機 [“via” atom] ;物理路徑 [“id” msg-id] ;接收者msg id 接受字段。是投遞信件的特定郵件服務器所作的記錄。處理郵件投遞的每個服務器必須給它處理的每個信頭的前面加一個Received字段,用以描述信件到達目的地所經過的路徑以及相關信息。當跟蹤各個電子郵件問題時,這個信息很有幫助。
舉例:Received:from wang[195.0.0.1] by li[129.5.0.4] Tue dec 2003 12:18:02 +800  
格式:Comments:*text 註釋字段。用於把一個註解添加到信件中。
格式:Resent-* 舉例:Resent-From Resent-Sender Resent-date Resent-Reply-To 重發字段。當需要把收到的信件重發給另一組收信人的時候,可以保持整個原始信件不變,並簡單地產生重發信件所要求的新信頭字段。爲避免與以前的字段相混。新添加的信頭字段都加上Resent-前綴字符串,它們的語法與未加前綴的同名字段相同。
格式:Message-ID:msg-id 信件標識字段。用於表示一個信件唯一標識,該字段通常有Smtp服務器生成,這個值通常是唯一的。形式根據使用的軟件而定。通常左邊是標識符,右邊指定電腦名

表中的關鍵字表明瞭電子郵件借用了辦公室備忘錄中的概念和術語:電子郵件的頭部能夠包含一行說明應當接收到該備忘錄的接收方。象傳統的辦公室備忘錄一樣,電子郵件使用關鍵字Cc指明一個複寫副本(carboncopy).電子郵件軟件必須向Cc:後面的電子郵件地址表中的每個地址發送一份消息的副本。

傳統的辦公室過程要求備忘錄的發送方通知接收方副本是否傳給其它人。有時發送方希望將備忘錄的一個副本給別人而不顯示出有一個副本被髮送出去。一些電子郵件系統提供這樣的選項,遵循傳統的辦公室術語,用盲複寫副本(blind carbon copy)來表示。創建消息的用戶

在關鍵字Bcc後給出一個電子郵件地址表,指定一個或多個盲複寫副本。雖然Bcc在發送方出現,但當信息發送時,郵件系統將它從消息中除去。每個接收方必須檢查頭部的To和Cc行以決定信息是直接發送還是作爲盲副本發送的(有些郵件系統在正文部分附加信息來告訴接收者它是一個盲副本)。其它接收者不知道有哪些用戶接收到盲副本。

電子郵件使用與傳統的辦公室備忘錄相同的格式和術語:頭部包括與消息有關的信息,正文包括消息文本。電子郵件頭部的行說明發送方、接收方、日期、主題、應當收到副本的人的列表。

擴展字段

如果想在信頭中加入RFC822中沒有規定的字段,就需要創建非標準字段。方法非常簡單,只要在自定義的信頭字段名的前面使用X-前綴。RFC822將這種方法稱爲擴展字段。 事實上已經有許多擴展字段被廣泛應用,但沒有標準定義。例如:

  • X-LOOP字段

    X-LOOP字段用來防止郵件的循環傳送。過濾或郵件列表處理程序,可以給它處理的每個信件增加一個X-LOOP字段,以後就可以根據這個字段中含有的特別值,判斷一個信件是否被循環傳送。如果確認郵件發生了循環,過濾或郵件列表處理程序就可以用不同的方式處理該信件。

  • X-Mailer字段

    X-Mailer字段用於指示什麼樣的程序產生了這個信件,它是使用最廣泛的擴展字段。產生郵件的軟件可以爲所有發送的信件增加合適的X-Mailer字段,該字段不僅含有軟件的名稱,還包含軟件的版本號。例如軟件名爲Littlefox Mailer,版本爲V1.0, 可以將“X-Mailer:Littlefox Mailer V1.0”加到郵件信頭中去。

下面列出了一些在因特網電子郵件中可以找到的普通關鍵字,以及使用它們的目的。

關鍵字 含義
From 發送方地址
To 接收方地址
Cc 複製副本地址
Date 信息創建日期
Subject 信息主題
Reply-To 回覆地址
X-Charset 使用的字符集(通常爲ASCII)
X-Mailer 發送信息所使用的軟件
X-Sender 發送方地址的副本
X-Face 經編碼的發送方面孔的圖象

整個系統的核心是收發信件的操作,因此爲了方便維護,以後的升級,故將這兩個最主要的操作寫成類庫(.dll)的形式,以組件的形式加載到主程序中,而且其它的功能如果需要的話,也可以通過這樣的組件的形式增加到主程序中。這也體現了C#這一新的微軟主推語言的方便和高效,而且這樣做也方便程序的順利結合。

5 命名控件MailSend

由於在C#語言中,都是以命名控件來組織程序的。而所有的類都歸屬於一個特定的命名空間下。需要的命名空間系統本身自帶了一部分,而且如果系統沒有你需要的命名空間的話,就可以自己編寫,本節中的這個命名空間就是由於需要而編寫的。而調用某一個類中的某個變量成員的方法就是通過命名空間名.類名.變量成員 來訪問的,當然在C#中如果在程序開始通過Using 命名空間名,就可以直接的象C++那樣來訪問成員變量,可以說相當的方便,這些都會在程序中體現出來,再次不再做過多的敘述。

5.1 發送郵件類SmtpMail

5.1.1 主要成員變量說明

1.網絡連接類及實例TcpClient tc

爲 TCP 網絡服務提供客戶端連接類TcpClient實例對象tc。TcpClient 類提供了一些簡單的方法,用於在同步阻塞模式下通過網絡來連接、發送和接收流數據。而實例化的過程也是連接SMTP服務器的過程。它的重載方法之一的兩個參數一個爲服務器名稱字符串,另一個爲服務器的埠。

2.提供用於網絡訪問的基礎數據流及其實例 NetworkStream ns

此類提供訪問網絡的基礎數據流的方法。其中最基本也是最重要的兩個方法就是Write()和Read()方法,至於參數不再次贅述。

3.一維字符串數組變量FilePath

此字符串數組主要用來存放用戶選擇的附件的絕對路徑名,並在發送帶附件的郵件時用到。

4.發送郵件所需的基本參數

比如用於ESMTP等錄檢驗用的用戶名、密碼,發送郵件需要的收信人,發信人地址以及主題等等在此不再贅述。

5.1.2 主要成員函數說明

1.重載的構造函數 SmtpMail()

此函數主要用於在初始化過程中,把用戶選擇的附件的路徑以參數的形式傳給FilePath。

2.添加附件的函數AddAttachment

傳給FilePath的路徑,通過這樣一個函數就可以循環的動態的添加到IList接口的一個對象中了,方便以後在具體的實現的過程中的使用。

3.得到上傳的附件的文件流GetStream

由於在網絡中的操作都是以網絡流的形式來實現的,因此先將上傳的附件轉換成文件流,然後再用Write的方法把這些附件的文件流寫入到網絡中,來完成發送附件的操作。具體實現代碼如下所示:


 
  1. private string GetStream(string FilePath)
  2. {
  3. //建立文件流對象
  4. System.IO.FileStream FileStr=new System.IO.FileStream(FilePath,System.IO.FileMode.Open);
  5. byte[] by=new byte[System.Convert.ToInt32(FileStr.Length)];
  6. FileStr.Read(by,0,by.Length);
  7. FileStr.Close();
  8. return(System.Convert.ToBase64String(by));
  9. }

4.將字符串編碼爲Base64字符串的函數Base64Encode

由於ESMTP的LOGIN認證機制是採用Base64編碼,當用戶發出AUTHLOGIN的命令後,服務器返回334的應答碼等待用戶輸入。如果身份確認後服務器返回235的應答碼,否則返回失敗信息。所以要將用戶名和密碼轉換成Base64編碼然後再發給服務器。此函數的作用就是把給定的字符串轉換成相應的Base64編碼的字符串。

5.發送SMTP命令的函數SendCommand

這個函數的作用是把SMTP命令的字符串轉換成對應的字節型值(C#中規定的Write方法只能寫入字節型的數據)然後寫入網絡中,如果操作成功就返回一個標誌爲真的布爾型變量,如果操作失敗或者發生異常就返回標誌爲假的布爾型變量。具體代碼如下所示:


 
  1. private bool SendCommand(string str)
  2. {
  3. //定義一個數組
  4. byte[] WriteBuffer;
  5. //設定一個布爾類型的變量
  6. bool state=false;
  7. WriteBuffer = Encoding.Default.GetBytes(str);
  8. //加入防錯機制,可以有效提高程序運行的效率和捕獲出錯信息
  9. try
  10. {
  11. //向網絡中寫入數據
  12. ns.Write(WriteBuffer,0,WriteBuffer.Length);
  13. state=true;
  14. }
  15. catch(Exception ex)
  16. {
  17. //返回出錯信息
  18. MessageBox.Show (ex.ToString ());
  19. state=false;
  20. }
  21. //返回標誌位
  22. return state;
  23. }

6.接受服務器應答的函數RecvResponse

它的作用就是從網絡流中讀取服務器返回的字節型的信息,將其轉換成字符串型的變量,然後將其返回,可以通過其返回值來判斷操作是否成功。具體實現代碼如下所示:


 
  1. private string RecvResponse()
  2. {
  3. int StreamSize=0;
  4. string ReturnValue ="";
  5. //定義一個字節型的數組
  6. byte[] ReadBuffer = new byte[1024] ;
  7. try
  8. {
  9. //從網絡流中讀取數據,並返回讀取的個數
  10. StreamSize=ns.Read(ReadBuffer,0,ReadBuffer.Length);
  11. }
  12. catch (Exception ex)
  13. {
  14. //返回異常信息
  15. MessageBox.Show(ex.ToString ());
  16. }
  17. if (StreamSize!=0)
  18. {
  19. //將當前讀取的信息轉換成字符串型然後返回
  20. ReturnValue= Encoding.Default.GetString(ReadBuffer).Substring(0,StreamSize);
  21. }
  22. return ReturnValue;
  23. }

7.重載的函數Dialog

它們的作用是與服務器交互,發送命令並接收回應。不同的是參數是字符串類型的那個函數,每次發送一條命令,並接受服務器的響應,根據響應的信息來判斷交互的結果是否成功。而參數是字符串數組的函數每次發送的是一組命令,用於和服務器的交互,這個函數主要是用於ESMTP服務器的驗證的功能,因爲驗證的過程是一個等待然後又輸入的過程,因此將他們放在一個數組中有利於理解和操作。而他們的實現主要是通過調用上面的發送SMTP命令函數SendCommand以及接受SMTP服務器響應的函數RecvResponse來實現的。具體的代碼如下所示:


 
  1. private bool Dialog(string str,string errstr)
  2. {
  3. bool flag=false;
  4. if(str==null||str.Trim()=="")
  5. {
  6. flag=true;
  7. }
  8. if(SendCommand(str))
  9. {
  10. string RR=RecvResponse();
  11. //從返回的數據中截取前三位
  12. string RRCode=RR.Substring(0,3);
  13. //然後用這前三位與哈希表中正確的迴應碼比較
  14. if(RightCodeHT[RRCode]!=null)
  15. {
  16. flag=true;
  17. }
  18. else
  19. {
  20. flag=false;
  21. }
  22. }
  23. else
  24. {
  25. flag=false;
  26. }
  27. return flag;
  28. }

發送一組命令主要用於服務器驗證的重載函數爲:


 
  1. private bool Dialog(string[] str,string errstr)
  2. {
  3. for(int i=0;i<str.Length;i++)
  4. {
  5. //循環調用單個的與服務器的交互過程
  6. if(!Dialog(str[i],""))
  7. {
  8. return false;
  9. }
  10. }
  11. return true;
  12. }

8.郵件發送程序SendMail

這是整個程序的核心部分。具體的實現SMTP協議的程序正是通過它一步一步實現並最終實現發送簡單郵件甚至帶附件的郵件的功能。而它的實現是調用以上給出的各個函數的結果。以下就簡單的通過幾個SMTP命令的格式來實現:


 
  1. private bool SendEmail()
  2. {
  3. //連接網絡
  4. try
  5. {
  6. //建立一個TCP連接
  7. tc=new TcpClient(mailserver,mailserverport);
  8. }
  9. catch
  10. {
  11. MessageBox.Show ("連接失敗","請確認");
  12. return false;
  13. }
  14. //獲取當前流的資料
  15. ns = tc.GetStream();
  16. SMTPCodeAdd();
  17. //驗證網絡連接是否正確
  18. if(RightCodeHT[RecvResponse().Substring(0,3)]==null)
  19. {
  20. return false;
  21. }
  22. string[] SendBuffer;
  23. string SendBufferstr;
  24. //進行SMTP驗證
  25. //具體的SMTP命令與代碼的結合
  26. if(ESmtp)
  27. {
  28. SendBuffer=new String[4];
  29. SendBuffer[0]="EHLO " + mailserver + enter;
  30. SendBuffer[1]="AUTH LOGIN" + enter;
  31. SendBuffer[2]=Base64Encode(username) + enter;
  32. SendBuffer[3]=Base64Encode(password) + enter;
  33. if(!Dialog(SendBuffer,"SMTP服務器驗證失敗,請覈對用戶名和密碼。"))
  34. return false;
  35. }
  36. else
  37. {
  38. SendBufferstr="HELO " + mailserver + enter;
  39. if(!Dialog(SendBufferstr,""))
  40. return false;
  41. }
  42. SendBufferstr="MAIL FROM:<" + From + ">" + enter;
  43. if(!Dialog(SendBufferstr,"發件人地址錯誤,或不能爲空"))
  44. return false;
  45. //把傳過來的收件人的地址分割然後提交給服務器
  46. string split=";";
  47. string []address=Regex.Split (Recipient,split);
  48. SendBuffer=new string [address.Length];
  49. for(int i=0;i<SendBuffer.Length;i++)
  50. {
  51. SendBuffer[i]="RCPT TO:<" +address[i]+">" + enter;
  52. }
  53. if(!Dialog(SendBuffer,"收件人地址有誤"))
  54. return false;
  55. SendBufferstr="DATA" + enter;
  56. if(!Dialog(SendBufferstr,""))
  57. return false;
  58. SendBufferstr="From:" + FromName + "<" + From +">" +enter;
  59. SendBufferstr += enter + "." + enter;
  60. if(!Dialog(SendBufferstr,"錯誤信件信息"))
  61. return false;
  62. SendBufferstr="QUIT" + enter;
  63. if(!Dialog(SendBufferstr,"斷開連接時錯誤"))
  64. return false;
  65. //關閉流對象
  66. ns.Close();
  67. //關閉連接
  68. tc.Close();
  69. FilePath=null;
  70. return true;
  71. }

以上即爲發送不帶附件的郵件SMTP命令用代碼實現的過程。

5.2 AddExtra類

這個附加的小類只是提供一些返回當前系統時間,獲取主機名,主機IP,有關幫助等小的功能,在此僅對幫助信息中的“關於”操作函數稍加說明。因爲它說明了在C#中調用 Windows API 函數所需如下幾個步驟:

5.2.1 調用Windows API 所需的命名空間


 
  1. using System.Runtime.InteropServices;

而調用顯示關於對話框的函數ShellAbout還需要用到兩個命名空間如下所示:


 
  1. using System.Reflection;
  2. using System.Diagnostics;

5.2.2 在程序中聲明所需的API函數


 
  1. [DllImport("shell32.dll")]
  2. static extern int ShellAbout(IntPtr hWnd, string szApp, string szOtherStuff,
  3. IntPtr hIcon);

5.2.3 在程序中具體的使用


 
  1. Assembly ass=Assembly.GetExecutingAssembly();
  2. FileVersionInfo myVersion=FileVersionInfo.GetVersionInfo(ass.Location );
  3. ShellAbout(this.Handle ,"郵件收發系統#","版本"+myVersion.FileMajorPart +"."+myVersion.FileMinorPart+"." +myVersion.CompanyName ,this.Icon .Handle );

至此就完成了在C#中調用 Windows API 函數的過程。

6 軟件運行時的界面

6.1 新建郵件帳號

用戶打開軟件之後,需要新建一個郵件帳號,在這個信件帳號的過程中,需要指定SMTP服務器,SMTP的端口,以及用於ESMTP驗證的用戶名和密碼。指定這些發郵件的必須參數之後,再回到系統的主界面如下所示:

6.2 發送郵件界面

6.2.1 發送不帶附件的郵件

在新建帳號的過程中已經指定了郵件地址,和帳號名稱,所以默認的以這些參數來發送郵件。通過調用參數的不同程序會自動的調用相對應的代碼來執行不同的操作。發送簡單的郵件運行界面如下。

6.2.2 發送帶附件的郵件

和簡單的郵件不同之處在於多了發送附件的功能,軟件模擬FoxMail裏面發送郵件時,在程序的下面自動顯示增添的附件的名稱,以及圖標等信息。並且郵件支持添加,刪除,排列圖標等功能。運行界面如下所示:

6.3 驗證郵件發送是否成功

郵件發送出去之後,用FoxMail跟蹤接收之後,證明郵件和附件都可以正常接收,具體的FoxMail的接收界面如下所示:

7 系統測試

我個人做的是這個軟件收發系統的一個最基本也是最主要的功能之一:發送郵件。

所以主要的測試也是圍繞發送郵件展開的,具體的可以分爲以下幾個方面。

7.1 同一SMTP服務器發送郵件的測試

這個方面的測試測的是,用戶登錄一個服務器(測試中用的是163的SMTP服務器)來發送一封郵件的測試。而這個測試又可以分爲以下兩個方面:

7.1.1 同一服務器,發送一封純文本郵件的測試

1.發送一封文本郵件給一個收信人

測試中用163的郵箱分別往163的郵箱以及新浪的郵箱發送郵件均可以用FoxMail正常的接收到發送的普通的純文本文件。

2.發送一封文本郵件給多個收件人

測試中仍然用163的郵箱同時發往不同的郵箱,通過FoxMail都可以正常的接收到。從而很好的驗證了,我們的郵件發送系統支持羣發的功能。

7.1.2 同一服務器,發送一封帶附件的郵件的測試。

1.發送一封帶附件(可以是多附件)的郵件給一個收件人

測試中用163的郵箱分別往163的郵箱以及新浪的郵箱發送之外,又添加了不同的郵件類型(個數分別爲等於1,大於1即驗證是否支持多附件的發送),用FoxMail接收之後,所有發送的純文本信息,以及附件信息都正常無誤。經過這些驗證可以證明本軟件支持對一個收件人發送多附件。由於帶有多附件的信件,所以寫入速度明顯慢於純文本郵件的速度。

2.發送一封帶附件(可以是多附件)的郵件給多個收件人

測試中用163的郵箱分別往163的郵箱以及新浪的郵箱發送之外,又添加了不同的郵件類型(個數分別爲等於1,大於1即驗證是否支持多附件的發送),用FoxMail接收之後,所有發送的純文本信息,以及附件信息都正常無誤。經過這些驗證可以證明本軟件支持對多個收件人發送多附件。

7.2 利用不同的SMTP服務器發送郵件的測試

這個方面的測試是指利用不同的郵箱來發送郵件,至於測試的分類雷同於利用同一服務器發送郵件的測試,所以不再此贅述。

總之,通過以上的各方面的測試,使我改正了代碼中的許多不合理以及錯誤之處,最終也證明了,我們的軟件系統是支持多種服務器,支持多附件發送的羣發軟件。

8 結論

這次編寫的郵件客戶端系統,我負責的是郵件的發送的功能。在熟悉了專門用於發送郵件的SMTP協議以及RFC規定的郵件的格式的基礎上,運用了微軟新推出的C#這一新型的面嚮對象語言的便利性和靈活性,從SMTP協議規定的底層命令做起,一步步的與服務器進行交互操作,最終實現發送多附件多接收人的功能。其中,具體的和服務器的交互操作,都封裝了在SmtpMail.dll這個動態鏈接庫裏面了。而爲了方便最終的調用和整合,所有的有關後臺操作發送郵件的類以及其他的附加功能的類,全部都歸屬於MailSend這個命名空間了。在力求達到FoxMail功能的同時,又加了一點個人的思想並把它體現到了這一軟件上。最主要的體現就是新建帳號的提前檢測這一特色上,這一功能類似於很多Web頁面的“檢測新帳號”的功能,這樣就免去了用戶一直到確定註冊完成時,才因爲帳戶因爲已經被使用而註冊失敗的麻煩。總之,通過這次的編程,使我對網絡編程有了一個很好的認識和鍛鍊,也使我對C#這一語言的掌握程度又上了一個新臺階,雖然編出來的軟件不能和功能強大的FoxMail相提並論,但是相信它簡單,易操作性,和FoxMail的很多強大但卻“雞肋”似的功能比較起來,更多了幾分實用性。以後的日子,隨着我技術的提高和思想的成熟,我一定會把它做的更好,更趨近於完美。

參考文獻

[1] Simon Robinson, K.Scott Allen等.C#高級編程. 北京:清華大學出版社, 2002,3

[2] Tom Archer. C#技術內幕. 北京:清華大學出版社, 2002,1

[3] 沉舟.Microsoft.NET編程語言C#. 北京:希望電子出版社. 2001,3

[4] 羅軍舟,黎波濤,楊明等.TCP/IP 協議及網絡編程技術. 北京: 清華大學出版. 2004,10

[5] Tim Parker .TCP/IP 協議及網絡編程技術. 北京: 機械工業出版社, 2000,7

[6] 周存傑. Visual C#.NET網絡核心編程. 北京:清華大學出版社, 2002,11

[7] 電腦編程技巧與維護雜誌社.C#編程技巧典型案例解析. 北京:中國電力出版社, 2005,8

[8] 雲顛工作室. Visual C#中文版全面剖析. 北京:中國水利水電出版社, 2003,5

[9] 葉樹華《電子協議與編程》,《電子郵件格式》,《電子郵件接收》,《mime 編碼解碼與發送附件》

[10] MSDN中文網站網絡廣播 C#設計模式縱談http://www.microsoft.com/china/msdn/events/webcasts/shared/Webcast/MSDNWebCast.aspx

[11] 滁州,馬金虎,朱力勇. 編寫基於SMTP網絡應用程序. 電腦愛好者,2003,5:92~94

[12] 滁州,馬金虎,朱力勇. 編寫基於POP3網絡應用程序. 電腦愛好者,2003,6:92~94

[13] 潘泰國. 新一代電子郵件系統. 電子技術應用. 1992,11

[14] 代繼紅. SMTP認證機制模塊化設計及實現. 中南民族大學學報(自然科學版), 2005,4

[15] 胡安廷. 簡單實現中文郵件. 中國計算機報, 2004,11

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