# [C# 網絡編程系列]專題十:實現簡單的郵件收發器
引言:
在我們的平常工作中,郵件的發送和接收應該是我們經常要使用到的功能的。因此知道電子郵件的應用程序的原理也是非常有必要的,在這一個專題中將介紹電子郵件應用程序的原理、電子郵件應用程序中涉及的協議和實現一個簡答的電子郵件收發器程序。
**一、郵件應用程序基本知識**
**1.1 電子郵件原理及相關協議**
說到電子郵件的原理,其實和我們現實生活中寄郵件和寄包裹是一樣的原理的。就讓我們先回顧下現實生活中寄郵件的流程吧——首先,我們先寫好信,信封上面寫好收信人的地址,寫信人的地址,然后把信放到寄信箱中,然后郵局的人會某個時候去這個信箱中的信取出來,然后郵局的人根據信封上寫的收信人地址進行轉發到當地的郵局,當地郵局然后把信寄到收信人的信箱中(_寄包裹的話可能會電話聯系,像我們在淘寶,京東買的東西的,收貨人就是通過電話聯系一樣_),最后收信人會到自己的信箱中收取信件。上面大致是我們平時生活中寄信的一個流程的。前面已經講過電子郵件的原理和這個差不多的,下面就介紹了本專題中電子郵件的原理,大家可以和現實生活中的寄信過程進行對比下的,這樣可以更加容易理解和掌握:
我們通過電子郵件應用(_例如 基于客戶端的Outlook電子郵件軟件 和一些基于Web的電子郵件系統——新浪郵箱、谷歌郵箱、QQ郵箱等都屬于電子郵件應用_)將一封寫好的郵件(_相當于現實生活中的信,當然郵件也要寫明收件人地址,郵件內容等信息的_)通過電子郵件協議(_SMTP,在后面的電子郵件相關協議中會介紹_)發送到SMTP服務器(_就是存儲郵件的地方,相當于生活中的郵局一樣_),然后SMTP服務器根據收件人的地址通過SMTP協議轉發到相應SMTP接收服務器上,(_SMTP服務器進行轉發相當于現實生活中郵局的人配送信的過程,配送到收件人當地的郵局,然而現實生活中郵局都是一家,所以可以相互識別——意思就是發送到當地郵局,當地郵局會接收,并且幫助你發送到指定人的信箱中,在網上上就是通過SMTP協議來規定這樣的一個過程的,發送到別人的SMTP服務器上別人的服務器必須要認識發送來的郵件并接收_)結束,接收端郵件服務器(POP3服務器)把郵件存放到接受者的電子信箱內(相當于當地郵局的人把信放到收信人的郵箱中),最后收件人可以登錄自己的電子信箱,再與POP3服務器進行連接,從POP3服務器上下載發送來的郵件,這樣在收件人的電子信箱中就可以看到發送來的電子郵件了(_這就是現實生活中收信人從自己的信箱中取信的一個過程_)。
_注:括號中都是個人的理解,如果有什么不對的地方還望大家指出來,我好及時更正。_
上面已經把電子郵件的原理和現實生活中寄信的過程進行對比,相信大家可以更加清楚電子郵件的原理和發送接收過程的,其實網絡上的很多應用都可以以現實生活的例子去理解,這樣的話我認為可以加深對知識的理解。下面就介紹下電子郵件中的相關協議的內容:
網絡上的應用的核心就是協議,因為協議讓網絡上的客戶端相互認識發生來的數據,所以電子郵件應用也不例外,也有相關的電子郵件協議來完成發送電子郵件和接收電子郵件的過程,這些協議主要是:SMTP(簡單郵件傳輸協議,Simple Mail Transfer Protocol)、POP3(郵局協議,Post Office Protocol)和IMAP(網絡郵件訪問協議,Internet Message Access Protocol)。
* SMTP——SMTP 主要負責將郵件從一臺機器轉發至另一臺機器(可以對照上面電子郵件的過程來理解SMTP的作用)
* POP3——3表示POP協議的版本,主要負責將郵件從郵箱中(POP3服務器)傳輸到本地計算機。
* IMAP——現在常用的版本為第四版本,即IMAP4,主要負責郵件的檢索和處理功能,客戶端不需要下載郵件到本地計算機,可直接從郵件客戶端軟件對服務器上的信件和文 件目錄進行操作,它是POP3的替代協議的。
**1.2 郵件系統的分類**
郵件系統主要分為兩類的——基于客戶端的郵件系統和基于Web瀏覽器的郵件系統。Office OutLook就是基于客戶端的郵件客戶端系統,而像我們經常使用的QQ郵箱、新浪、網易郵箱等都是屬于基于Web瀏覽器的郵件系統,基于客戶端的郵件系統的收發過程,通過下面的圖片來描述(圖片從網上摘下的):

圖 1.1 基于客戶端的郵件收發過程
發送方通過郵件客戶端,將編輯好的郵件向郵件服務(SMTP服務器,在發送過程中也叫發送端郵件服務器)發送,發送端郵件服務器根據收件人的地址來識別接收端郵件服務器(POP3服務器),然后向POP3服務器發送郵件信息,接收端郵件服務器將郵件存放在接收者的電子信箱中,并告知接收者有新郵件,接收者通過郵件客戶端與POP3服務器連接后,就可以查看新郵件。
然而,基于Web瀏覽器的郵件系統與基于客戶端的郵件系統不同的地方有:
* 基于Web瀏覽器郵件系統用戶代理(代理的概念也就是用戶不是直接與服務器進行通信,而是通過代理的方式,讓代理去與服務器通信,然后用戶在從代理中獲的服務器的信息,代理也就是中間人的作用,相當于生活中中介,在.net中很多技術都用到了代理,例如委托的概念其實也就是代理的一個概念的)是Web瀏覽器,基于客戶端的郵件系統而是郵件客戶端應用程序,一般是Windows Form程序。
* 瀏覽器發送郵件到SMTP服務器和從POP3服務器中獲得郵件的方式都是通過HTTP協議來實現,與基于客戶端的郵件系統不同(基于客戶端的郵件系統發送通過SMTP協議或ESMTP(Extended SMTP),獲得通過POP3或IMAP協議)。
**1.3 目前主要的電子郵件服務系統**
電子郵件服務系統——就是向大家提供郵箱服務的服務系統,這樣的系統當然是由專門的公司進行研發的,我們一般叫這樣的公司為郵件服務商,我們平常使用的網易郵箱,新、Gmail郵箱等都是建立在電子郵件服務系統(_這里我的理解是——我們使用的新浪,網易等郵箱相當于現實生活中每個人的信箱,通過信箱可以獲得郵局來的信,同樣道理通過郵箱可以獲得郵件服務系統的郵件,這樣電子郵件系統相當于郵局_) 。現在主要電子郵件服務系統主要有下面幾種:
* 基于Postfix/Qmail的郵件系統。例如,雅虎郵箱基于Qmail系統
* 微軟Exchange 郵件系統
* IBM Lotus Domino郵件系統
* Scalix郵件系統
* Zimbra郵件系統
* MDeamon郵件系統
**二、.Net 平臺對郵件發送功能的支持**
在.NET類庫中,在System.Net.Mail命名空間下定義了對郵件處理的類,這樣使郵件的發送更加方便(_這些類也就是對SMTP協議的封裝,使我們更好地區編程,只需要使用類中的方法和屬性等去完成郵件的發送,避免寫復雜的SMTP協議的命令_),下面是一張在System.Net.Mail命名空間下對郵件發送的支持的類截圖:

從圖片中類的名字中也可以看出每個類的作用的,在這里我就不一個介紹的, 大家可以參考MSDN去看每個類的使用,并且我在后面程序的實現部分也會有詳細的注釋去介紹程序中使用到類的使用。從圖中還可以i看出一點——就是只有SMTP的字樣,卻沒有POP3這樣的字樣的,這說明.Net類庫本身中并沒有提供對POP3協議的封裝類,但是我們可以使用Jmail組件來完成從POP3服務器中收取郵件的功能,具體的使用將在后面的郵件收發器程序中郵件的接收部分介紹的。
**三、郵件收發器程序的實現**
**3.1 郵件發送功能的實現**
**3.1.1 SMTP協議**
SMTP 協議是用于電子郵件的傳輸的協議,電子郵件是通過SMTP服務器進行發送的,SMTP服務器的默認端口為25,通常發送郵件有兩種方式——一種是不使用客戶端認證,即客戶端可以使用匿名發送郵件(這種方式叫做SMTP);另一種是客戶端必須提供用戶名和密碼認證(這種方式叫做ESMTP,Extended SMTP)目前大部分郵件服務器采用用戶名和密碼認證的方式。
客戶端發送郵件過程為——先通過客戶端軟件(本程序中的郵件收發器)將郵件發送到SMTP服務器,然后再由SMTP服務器發送到目標SMTP服務器。下面介紹SMTP協議的內容:
SMTP協議總共定義了14個命令,命令由命令碼和氣候的參數域組成, 不區別大小寫的(通過前面專題的講述可以得出各個協議的命令組成都差不多的),下面就簡單介紹下5個常用的命令碼
|名稱 |解釋 |
| --- | --- |
|HELO或EHLO |發送連接到服務器的命令,EHLO主要用于與ESMTP服務器建立連接時發送的命令 |
|MAIL FROM |指定發件人的郵件地址 |
|Rcpt to |指定收件人的郵件地址 |
|Data |指定郵件正文內容,郵件內容以單獨一行 ”.” 表示接觸 |
|Quit |關閉與服務器的連接,然后退出 |
電子郵件由信封、首部、正文和結束符號4部分組成,下面就具體介紹下這4個部分的內容:
1\. 信封
信封包括發信人的郵件地址和接收人的郵件地址,具體對應兩條SMTP命令——Mail from: [mytest1989@sina.cn](mailto:mytest1989@sina.cn)(發信人的地址)和Rcpt to: [794170314@qq.com](mailto:794170314@qq.com)
2\. 首部
首部中常用的命令有:
* Subject:<郵件主題>——表示郵件的主題
* Date:<時間>——表示發郵件的時間
* reply-to:<郵件地址>——表示郵件的回復地址
* Content-Type:<郵件類型>——表示郵件包含文本、HTML超文本和附件的類型。
* X-Priority:<郵件優先級>——表示郵件發送的優先級,優先級為3表示為普通郵件;如 X-Priority:3
3\. 正文
正文當然指的就是郵件的內容了, 用Data命令指定,首部以一個空行結束,下面就是正文部分
4\. 結束符號
郵件以“."結束,
接收方收到SMTP命令之后,會給出一個響應碼,每個命令都只有一個響應碼,SMTP響應碼也是由3位數字組成,后面附加一些文本信息,響應信息的格式為:
響應碼<空格>文本信息<回車換行>
客戶端發出一條命令后,服務器端返回一個響應,發送者在發送下一條命令前必須等待服務器的響應,成功接收到響應碼后才繼續發送命令。
附:SMTP常用的響應碼:
|響應碼 |解釋 |響應碼 |解釋 |
| --- | --- | --- | --- |
|211 |系統狀態或系統幫助響應 |421 |服務未就緒,關閉了傳輸通道 |
|214 |幫助信息 |501 |參數格式錯誤 |
|220 |服務就緒 |502 |命令不可實現 |
|221 |服務關閉傳輸通道 |535 |用戶驗證失敗 |
|235 |用戶驗證成功 |553 |郵箱名不可用,要求的操作未執行 |
|334 |等待用戶輸入驗證 |554 |操作失敗 |
|354 |開始郵件輸入 |
**3.1.2 郵件的發送過程**
第一步:客戶端與服務器建立連接(該步中客戶端首先發送EHLO local 連接命令,服務器如果返回“220”響應碼表示服務器準備就緒了,客戶端再繼續發送“Auto login”命令,請求登錄,服務器收到命令后返回“334”響應碼,表示要輸入用戶名,之后客戶端發送用戶名命令,等到響應后再發送密碼命令,具體在程序的實現中也會有注釋。)
第二步:客戶端發送郵件的信封
第三步:開始發送郵件數據,(包括郵件首部,正文和結束符號,注:結束符號要單獨占一行,表示郵件發送結束)
第四步: 客戶端與服務器斷開連接。
**3.1.3 發送功能的實現代碼**
相信有了上面的理論解釋郵件發送的過程后,實現郵件發送的功能并不難的,并且.net類庫中SMTPClient類幫我們封裝了SMTP協議,使得我們實現郵件發送功能就不要記住那些具體的命令了, 只需要使用該類中提供的方法來完成郵件的發送(當然你也可以通過發送命令的方式實現,SMTPClient類的方法也是幫我們完成發送命令功能而已的),下面是郵件發送功能的核心代碼:
View Code
```
1 #region 郵件發送功能代碼
2 // 添加附件
3 private void btnAddFile_Click(object sender, EventArgs e)
4 {
5 OpenFileDialog openFileDialog = new OpenFileDialog();
6 openFileDialog.CheckFileExists = true;
7 // 只接受有效的文件名
8 openFileDialog.ValidateNames = true;
9 // 允許一次選擇多個文件作為附件
10 openFileDialog.Multiselect = true;
11 openFileDialog.Filter = "所有文件(*.*)|*.*";
12 if (openFileDialog.ShowDialog() != DialogResult.OK)
13 {
14 return;
15 }
16 if (openFileDialog.FileNames.Length > 0)
17 {
18 // 因為這里允許選擇多個文件,所以這里用AddRange而沒有用Add方法
19 cmbAttachment.Items.AddRange(openFileDialog.FileNames);
20 }
21 }
22
23 // 刪除附件
24 private void btnDeleteFile_Click(object sender, EventArgs e)
25 {
26 int index = cmbAttachment.SelectedIndex;
27 if (index == -1)
28 {
29 MessageBox.Show("請選擇要刪除的附件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
30 return;
31 }
32 else
33 {
34 cmbAttachment.Items.RemoveAt(index);
35 }
36 }
37
38 // 發送郵件
39 private void btnSend_Click(object sender, EventArgs e)
40 {
41 this.Cursor = Cursors.WaitCursor;
42 // 實例化一個發送的郵件
43 // 相當于與現實生活中先寫信,程序中把信(郵件)抽象為郵件類了
44 MailMessage mailMessage = new MailMessage();
45 // 指明郵件發送的地址,主題,內容等信息
46 // 發信人的地址為登錄收發器的地址,這個收發器相當于我們平時Web版的郵箱或者是OutLook中配置的郵箱
47 mailMessage.From = new MailAddress(tbxUserMail.Text);
48 mailMessage.To.Add(txbSendTo.Text);
49 mailMessage.Subject = txbSubject.Text;
50 mailMessage.SubjectEncoding = Encoding.Default;
51 mailMessage.Body = richtbxBody.Text;
52 mailMessage.BodyEncoding = Encoding.Default;
53 // 設置郵件正文不是Html格式的內容
54 mailMessage.IsBodyHtml = false;
55 // 設置郵件的優先級為普通優先級
56 mailMessage.Priority = MailPriority.Normal;
57 //mailMessage.ReplyTo = new MailAddress(tbxUserMail.Text);
58
59 // 封裝發送的附件
60 System.Net.Mail.Attachment attachment = null;
61 if (cmbAttachment.Items.Count > 0)
62 {
63 for (int i = 0; i < cmbAttachment.Items.Count; i++)
64 {
65 string fileNamePath = cmbAttachment.Items[i].ToString();
66 string extName = Path.GetExtension(fileNamePath).ToLower();
67 if (extName == ".rar" || extName == ".zip")
68 {
69 attachment = new System.Net.Mail.Attachment(fileNamePath, MediaTypeNames.Application.Zip);
70 }
71 else
72 {
73 attachment = new System.Net.Mail.Attachment(fileNamePath,MediaTypeNames.Application.Octet);
74 }
75
76 // 表示MIMEContent-Disposition標頭信息
77 // 對于ContentDisposition具體類的解釋大家可以參考MSDN
78 // 這里我就不重復貼出來了,給個地址: http://msdn.microsoft.com/zh-cn/library/System.Net.Mime.ContentDisposition.aspx (著重看備注部分)
79 ContentDisposition cd = attachment.ContentDisposition;
80 cd.CreationDate = File.GetCreationTime(fileNamePath);
81 cd.ModificationDate = File.GetLastWriteTime(fileNamePath);
82 cd.ReadDate = File.GetLastAccessTime(fileNamePath);
83 // 把附件對象加入到郵件附件集合中
84 mailMessage.Attachments.Add(attachment);
85 }
86 }
87
88 // 發送寫好的郵件
89 try
90 {
91 // SmtpClient類用于將郵件發送到SMTP服務器
92 // 該類封裝了SMTP協議的實現,
93 // 通過該類可以簡化發送郵件的過程,只需要調用該類的Send方法就可以發送郵件到SMTP服務器了。
94 smtpClient.Send(mailMessage);
95 MessageBox.Show("郵件發送成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
96
97 }
98 catch(SmtpException smtpError)
99 {
100 MessageBox.Show("郵件發送失敗:[" + smtpError.StatusCode + "];["
101 + smtpError.Message+"];\r\n["+smtpError.StackTrace+"]."
102 ,"錯誤",MessageBoxButtons.RetryCancel,MessageBoxIcon.Error);
103 }
104 finally
105 {
106 mailMessage.Dispose();
107 this.Cursor = Cursors.Default;
108 }
109 }
110
111 #endregion
```
**3.2 郵件接收功能的實現**
**3.2.1 POP3協議**
前面介紹了郵件的發送,當然接收者需要登錄郵箱來查看收到的郵件了,此時就必有有一個協議去讀取服務器上郵件,POP3就是這樣的一個協議。還有兩外一種協議也是用來接收郵件的——IMAP協議,它與POP3協議區別有:1\. IMAP使用的端口號是143而POP3郵件服務器通過監聽110端口來提供POP3服務;
2 . IMAP 允許客戶端在郵件服務器上建立文件夾來保持郵件,而不用把郵件下載到本地。而POP3需要把郵件下載到本地。
和SMTP協議一樣,客戶端要通過POP3協議從POP3服務器上獲取郵件,也需要先與POP3服務器建立TCP連接,等待服務器向客戶端發送確認信息表明連接成功時,客戶端才可以繼續發送命令給服務器來獲取郵件,在POP3協議中,規定的命令也是幾十條的,每條命令由命令和參數兩部分組成,都是以回車換行結束,并且命令和參數之間由空格分隔,命令通常也是由3-4個字母組成,參數最多可以為40個字符的長度,而服務器的響應信息是由一個狀態碼和**可能**附加信息的字符組成,所有的響應信息也是以回車換行結束的。狀態碼和其他協議定義的狀態碼有點不一樣,POP3服務器響應的狀態碼有兩種——“+OK”(確定)和"-ERR"(失敗)。這樣客戶端可以通過檢查響應的狀態碼所包含的字符來判斷服務器是否響應客戶端發送的命令,即響應信息中包含“+OK”表示成功響應,包含“-ERR”表示服務器未響應。同時在程序的實現中大家可以通過Debug來查看響應消息的組成,這樣可以加深理解。
**3.2.2 郵件接收的過程**
客戶端從服務器接收郵件的過程主要經歷3個狀態:授權狀態、操作狀態和更新狀態
(1)授權狀態——客戶端發送與POP3服務器的TCP連接請求,服務器接收后發送一個響應確認信息之后,此時客戶端需要發送正確的用戶名和密碼進行確認,因為在郵件服務器上有很多用戶郵箱,只有提供正確的用戶名和密碼才有權限訪問自己的郵箱,就像現實生活中我們郵箱的鑰匙一樣的。
發送用戶名命令: USER mytest1989@sina.cn
發送密碼命令: PASS ******(這兩個命令都在代碼中有給出的,大家可以參考代碼來理解郵件的接收過程)
(2) 操作狀態——如果客戶端提供了正確的用戶名和密碼,則授權狀態也就通過了,就相當于打開了在服務器上自己的郵箱,現在用戶就有權限進去下載,查看和刪除郵件等操作的,然后在現實生活中的取郵件和刪除郵件都很簡單(只要打開了郵箱門,用手去拿就可以了),然后在網絡應用上,這些操作都需要發送POP3命令給服務器,服務器接收到命令后再給出響應。操作中常用的命令有:
* STAT 命令——該命令從服務器中獲取郵件總數和總字節數,服務器響應命令返回郵件總數和總字節數
如:
```
客戶端發送POP3命令: STAT
服務器響應命令: +OK 2 1340
服務器響應命令:
```
* LIST 命令——該命令從服務器中獲得郵件列表和大小,服務器響應命令返回列出郵件列表和大小。
如:
```
客戶端發送POP3命令:LIST
服務器響應命令: +OK 2 message(1430 octect)
服務器響應命令:1 700
服務器響應命令:2 730
服務器響應命令:<一個空行>
```
* RETR 命令—— 該命令從服務器中獲得一個郵件,格式為 RETR <郵件編號>
如:
```
客戶端發送POP3命令:RETR 1
服務器響應命令: 700 octets
服務器響應命令:<郵件頭和內容>
服務器響應命令: <空行>
```
* DELETE 命令——該命令告訴服務器將郵件標記為刪除。(此時只是邏輯刪除)
(3)更新狀態——客戶端發送QUIT命令后,此時就進入更新狀態,POP3服務器釋放在操作狀態中取得的資源,并將邏輯刪除的郵件進行物理刪除,然后關閉與客戶端的TCP連接。這樣整個郵件處理的過程就結束了。
**3.2.3 接收功能的實現代碼**
有了前面接收郵件過程的介紹,再參考代碼的實現,相信大家可以更好的理解客戶端從POP3服務器中獲取郵件的過程的,由于.net類庫并沒有幫我們封裝POP3協議的實現類,要實現郵件的獲取可以采用發送命令的方式,也可以使用Jmail組件,這個組件其實就是POP3協議的封裝類,既然微軟沒有幫我們做,其他公司幫我們做好后來幫助我們簡單的實現郵件的接收的一個類庫罷了。然后在使用這個組件的過程中出現了好幾個問題的,在源碼中我都解釋,大家可以下載源代碼后查看的。
實現郵件接收的核心代碼如下:
View Code
```
// 登錄郵箱(這里是本程序——郵件收發器)
private void btnLogin_Click_1(object sender, EventArgs e)
{
// 與POP3服務器建立TCP連接
// 建立連接后把服務器上的郵件下載到本地
// 設置當前界面的光標為等待光標(就是我們看到的一個動的圓形)
Cursor.Current = Cursors.WaitCursor;
lsttbxStatus.Items.Clear();
try
{
// POP3服務器通過監聽TCP110端口來提供POP3服務的
// 向POP3服務器發出tcp請求
tcpClient = new TcpClient(tbxPOP3Server.Text, 110);
lsttbxStatus.Items.Add("正在連接...");
}
catch
{
MessageBox.Show("連接失敗", "錯誤", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
lsttbxStatus.Items.Add("連接失敗!");
return;
}
// 連接成功的情況
networkStream = tcpClient.GetStream();
streamReader = new StreamReader(networkStream, Encoding.Default);
streamWriter = new StreamWriter(networkStream, Encoding.Default);
streamWriter.AutoFlush = true;
string str;
// 讀取服務器返回的響應連接信息
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("服務器拒接了連接請求");
return;
}
// 如果服務器接收請求
// 向服務器發送憑證——用戶名和密碼
// 向服務器發送用戶名,請求確認
lsttbxStatus.Items.Add("核實用戶名階段...");
SendToServer("USER " + tbxUserMail.Text);
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("用戶名錯誤.");
return;
}
// 用戶名審核通過后再發送密碼等待確認
// 向服務器發送密碼,請求確認
SendToServer("PASS "+txbPassword.Text);
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("密碼錯誤!");
return;
}
lsttbxStatus.Items.Add("身份驗證成功,可以開始會話");
// 向服務器發送LIST 命令,請求獲得郵件列表和大小
lsttbxStatus.Items.Add("獲取郵件....");
SendToServer("LIST");
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("獲取郵件列表失敗");
return;
}
lsttbxStatus.Items.Add("郵件獲取成功");
// 窗口控件控制
tabControlMyMailbox.Enabled = true;
btnReadMail.Enabled = false;
btnDownLoad.Enabled = false;
btnDeleteMail.Enabled = false;
// 登陸成功后實例化郵件發送對象,以便后面完成發送郵件的操作
// 實例化郵件發送類(SmtpClient)對象
if (smtpClient == null)
{
smtpClient = new SmtpClient();
smtpClient.Host = tbxSmtpServer.Text;
smtpClient.Port = 25;
// 不使用默認憑證,即需要認證登陸
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential(tbxUserMail.Text, txbPassword.Text);
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
}
// 登陸成功后,自動接收新郵件
// 開始接收郵件
try
{
btnRefreshMailList.PerformClick();
}
catch
{
MessageBox.Show("讀取郵件列表失敗!", "錯誤", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
}
lsttbxStatus.Items.Add("登陸成功!");
lsttbxStatus.TopIndex = lsttbxStatus.Items.Count - 1;
Cursor.Current = Cursors.Default;
// 窗口控件控制
richtbxMailContentReview.Enabled = true;
tbxUserMail.Enabled = false;
txbPassword.Enabled = false;
btnLogin.Enabled = false;
btnLogout.Enabled = true;
tbxSmtpServer.Enabled = false;
tbxPOP3Server.Enabled = false;
btnReadMail.Enabled = true;
btnDownLoad.Enabled = true;
btnDeleteMail.Enabled = true;
tabControlMyMailbox.Focus();
}
#region 處理與POP3服務器交互事件
// 獲取服務器響應的信息
private string GetResponse()
{
string str = null;
try
{
str = streamReader.ReadLine();
if (str == null)
{
lsttbxStatus.Items.Add("連接失敗——服務器沒有響應");
}
else
{
lsttbxStatus.Items.Add("收到:[" + str + "]");
if (str.StartsWith("-ERR"))
{
str = null;
}
}
}
catch(Exception err)
{
lsttbxStatus.Items.Add("連接失敗:[" + err.Message + "]");
}
return str;
}
// 檢查響應信息
private bool CheckResponse(string responseString)
{
if (responseString == null)
{
return false;
}
else
{
if (responseString.StartsWith("+OK"))
{
return true;
}
else
{
return false;
}
}
}
// 向服務器發送命令
private bool SendToServer(string str)
{
try
{
// 這里必須使用WriteLine方法的,因為POP3協議中定義的命令是以回車換行結束的
// 如果客戶端發送的命令沒有以回車換行結束,POP3服務器就不能識別,也就不能響應客戶端的請求了
// 如果想用Write方法,則str輸入的參數字符中必須包含“\r\n”,也就是回車換行字符串。
streamWriter.WriteLine(str);
streamWriter.Flush();
lsttbxStatus.Items.Add("發送:[" + str + "]");
return true;
}
catch(Exception ex)
{
lsttbxStatus.Items.Add("發送失敗:[" + ex.Message + "]");
return false;
}
}
#endregion
```
**3.3 程序運行結果演示**
首先輸入郵箱名和密碼登錄到POP3服務器來獲取郵件列表的演示:

然后在郵件列表中選中一個郵件進行閱讀,然后進行回復郵件的操作演示(郵件的發送都可以附加附件發送出去):
閱讀郵件的界面:

回復郵件的界面:

同時點擊發送按鈕后,就可以把郵件發送到sina的SMTP服務器上,再由新浪的SMTP服務器轉發到QQ的SMTP服務器,QQ的POP3服務器中QQ的SMTP服務器獲取收到的郵件,當QQ用戶輸入正確的郵箱名和密碼后就可以從QQ的POP3服務器上獲取收到的郵件。
點擊發送按鈕后成功發送郵件的圖片:

**四、總結**
介紹到這里,本專題的內容就已經介紹完了,希望通過本專題可以讓大家明白郵件發送和接收的原理,并且可以自定義一個簡單郵件收發器的功能的,在后面一專題將介紹FTP協議(文件傳輸協議),并實現一個簡單的文件上傳和下載的程序。
源代碼下載地址: [http://files.cnblogs.com/zhili/MailSendAndReceive.zip](http://files.cnblogs.com/zhili/MailSendAndReceive.zip) ,如果覺得有幫助的話,還望大家推薦下,謝謝大家的支持
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結