微軟的VS.Net開發工具推出已有一段時日了,其中的那門新語言C#也日漸為大家所熟悉并接受。C#作為一門新興的語言相信具有傳統語言不可比拟的優越性,特别是在網絡應用方面,開發者更是感覺到了C#的強大功能。是以本文就通過運用C#來實作一個基于POP3協定的郵件接收程式來向大家展示C#網絡程式設計的功能強大,同時也向大家介紹一下基于POP3協定的電子郵件接收原理。
首先我向大家介紹郵件接收的基本原理:
一開始便是用戶端與伺服器的連接配接。不過,在用戶端連接配接到伺服器之前,注意把端口設為POP3協定預設的110号。
用戶端連接配接伺服器成功後,伺服器會傳回以下資訊:
+OK……
字元+OK是POP3協定的傳回資訊。它的回應資訊不像SMTP協定那樣用豐富多變的數字表示,隻有兩個:+OK或者-ERR。其中,+OK表示連接配接成功,而-ERR則表示連接配接失敗。
接下來,用戶端輸入USER <使用者名>
該指令告訴伺服器你的使用者名。注意,有些伺服器會區分大小寫字母的。
伺服器傳回+OK後,用戶端輸入PASS <密碼>
伺服器傳回+OK後,還傳回一些郵箱的統計資訊,比如:+OK 1 message(s) [1304 byte(s)]
不同的伺服器傳回的資訊格式不太一樣,是以我們可以用STAT指令來檢視郵箱的情況。STAT指令的回應中有兩個數字,分别表示郵件的數量和郵件的大小。
如果信箱裡有信,就可以用RETR指令來擷取郵件的正文。RETR指令的格式為:
RETR <郵件編号>
如果傳回結果第一行是+OK資訊,則表示成功。第二行起便是郵件的正文。最後一行和SMTP協定一樣,是一個單獨的英文句号,表示郵件的結尾部分。
把郵件存儲起來後要用DELE指令删除郵箱中的郵件,否則原有的郵件會繼續保留在伺服器上,一旦郵件一多,你的郵箱就爆了。DELE指令的格式為:
DELE <郵件編号>
如果删錯了,可以用RSET指令來恢複所有已被删除的郵件。條件是你還沒有退出,一旦退出,那就一切Bye Bye了。全部完成以後,輸入QUIT指令就可以退出POP3伺服器了。
實作
以上,我簡要地向大家介紹了POP3郵件接收的基本過程和原理,下面就是一個運用上面的原理實作的簡易的郵件接收程式。有了對基本原理的了解,程式設計的工作就變得相當輕松了。在本程式中,我主要用到了兩個類:TcpClient類和NetworkStream類。TcpClient類是運用C#進行網絡程式設計的一個非常重要的類,它提供了通過網絡連接配接、發送和接收資料的簡單方法,進而實作了網絡程式設計的大大簡化。NetworkStream類實作通過網絡套接字發送和接收資料的标準.Net架構流機制,它支援對網絡資料流的同步和異步通路,是實作網絡通訊的重要組成部分。在這裡我先給出程式最終的運作效果,圖示如下:
具體的過程步驟如下:
第一步:打開VS.Net,建立一個工程,在項目類型裡選擇“Visual C#項目”,在模闆裡選擇“Windows 應用程式”,工程名不妨為“MailReceiver”,最後點選“确定”按鈕。
第二步:布置主界面。先往窗體上添加如下控件:六個Label控件、四個TextBox控件、一個RichTextBox控件、一個CheckBox控件、一個ListBox控件以及三個Button控件。各個控件的屬性設定如下表所示:
Form1(主窗體) | Text屬性 | POP3郵件接收程式 |
MaximizeBox屬性 | False | |
Label1 | Text屬性 | POP3 伺服器位址: |
TextAlign屬性 | MiddleRight | |
Label2 | Text屬性 | 使用者名: |
TextAlign屬性 | MiddleRight | |
Label3 | Text屬性 | 密碼: |
TextAlign屬性 | MiddleRight | |
Label4 | Text屬性 | 資訊: |
TextAlign屬性 | MiddleLeft | |
Label5 | Text屬性 | 郵件編号: |
TextAlign屬性 | MiddleLeft | |
Label6 | Text屬性 | 狀态: |
TextAlign屬性 | MiddleLeft | |
PopServer、Username、Password、MailNum(TextBox控件) | Text屬性 | (均為空) |
Password的PasswordChar屬性 | * | |
Message(RichTextBox控件) | Text屬性 | (為空) |
BackupChBox(CheckBox控件) | Text屬性 | 在郵件伺服器上保留備份 |
Status(ListBox控件) | ItemHeight屬性 | 12 |
Connect(Button控件) | Text屬性 | 連接配接 |
FlatStyle屬性 | Flat | |
Disconnect (Button控件) | Text屬性 | 斷開連接配接 |
FlatStyle屬性 | Flat | |
Enabled屬性 | False | |
Retrieve(Button控件) | Text屬性 | 收取郵件 |
FlatStyle屬性 | Flat | |
Enabled屬性 | False |
其他屬性均可為預設值。在設定完以上的各個控件屬性後,對主窗體進行合理的布局,則可得到最終的界面。
第三步:代碼編寫。首先因為程式中運用到網絡程式設計的一些重要的類,如:TcpClient、NetworkStream,是以在程式的開頭處需添加以下名字空間(Namespace):
using System.Net;
using System.Net.Sockets;
using System.IO;
接着,為我們的類添加以下一些公有的資料成員:
public TcpClient Server;
public NetworkStream NetStrm;
public StreamReader RdStrm;
publicstring Data;
publicbyte[] szData;
publicstring CRLF = "/r/n";
最後,便是三個按鈕的消息響應函數,這三個函數是程式的主體部分。其中Connect按鈕的消息響應函數完成了到POP3伺服器的連接配接工作,并根據使用者名和密碼進行登入。如果一切順利,則再向伺服器發送STAT指令進而獲得郵箱中郵件的相關資訊:郵件數量和大小,最終完成連接配接。其函數實作如下:
privatevoid Connect_Click(object sender, System.EventArgs e)
{
//将光标置為等待狀态
Cursor cr = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
//用110端口建立POP3伺服器連接配接
Server = new TcpClient(PopServer.Text,110);
Status.Items.Clear();
try
{
//初始化
NetStrm = Server.GetStream();
RdStrm= new StreamReader(Server.GetStream());
Status.Items.Add(RdStrm.ReadLine());
//登入伺服器過程
Data = "USER "+ Username.Text+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
Data = "PASS "+ Password.Text+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
//向伺服器發送STAT指令,進而取得郵箱的相關資訊:郵件數量和大小
Data = "STAT"+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
//改變按鈕的狀态
Connect.Enabled = false;
Disconnect.Enabled = true;
Retrieve.Enabled = true;
//将光标置回原來的狀态
Cursor.Current = cr;
}
catch(InvalidOperationException err)
{
Status.Items.Add("Error: "+err.ToString());
}
}
而Disconnect按鈕的消息響應函數則通過向伺服器發送QUIT指令來終止和郵件伺服器的連接配接,實作比較簡單,具體函數如下:
privatevoid Disconnect_Click(object sender, System.EventArgs e)
{
//将光标置為等待狀态
Cursor cr = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
//向伺服器發送QUIT指令進而結束和POP3伺服器的會話
Data = "QUIT"+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
//斷開連接配接
NetStrm.Close();
RdStrm.Close();
//改變按鈕的狀态
Connect.Enabled = true;
Disconnect.Enabled = false;
Retrieve.Enabled = false;
//将光标置回原來的狀态
Cursor.Current = cr;
}
最後,Retrieve按鈕的消息響應函數則根據使用者在郵件編号文本框中的輸入來取得相應的郵件,它向伺服器發送的指令為RETR,并根據傳回的資訊判斷郵件的有無。若傳回資訊的第一個字元為“+”,則表示+OK,也即成功;若為“-”,則表示-ERR,也即為失敗。在成功的情況下,若使用者選擇了“在郵件伺服器上保留備份”,則收取郵件後不删除伺服器上的備份,否則用DELE指令來進行相應的删除。函數實作如下:
privatevoid Retrieve_Click(object sender, System.EventArgs e)
{
//将光标置為等待狀态
Cursor cr = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
string szTemp;
Message.Clear();
try
{
//根據郵件編号從伺服器獲得相應郵件
Data = "RETR "+ MailNum.Text+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
szTemp = RdStrm.ReadLine();
if(szTemp[0]!='-')
{
//不斷地讀取郵件内容,隻到結束标志:英文句号
while(szTemp!=".")
{
Message.Text += szTemp;
szTemp = RdStrm.ReadLine();
}
//若BackupChBox未選中,則收取郵件後,删除保留在伺服器上的郵件
if(BackupChBox.Checked == false)
{
Data = "DELE" + MailNum.Text + CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
}
}
else
{
Status.Items.Add(szTemp);
}
//将光标置回原來的狀态
Cursor.Current = cr;
}
catch(InvalidOperationException err)
{
Status.Items.Add("Error: "+err.ToString());
}
}
最後一步便是儲存你的勞動成果,再進行編譯、建立并運作了,最終的圖示我已經在文章的開頭處給出了,還行吧。
到處為止,我們已經完成了POP3郵件接收程式的全部工作了。從中,我們不難發現運用C#完成一些網絡實用程式設計是非常容易的。隻要我們掌握了其中的原理,具體的程式設計工作就變得非常的輕松而且有條理。即使以前你對POP3郵件接收的實作一無所知,我想在看完這篇文章後,你對它至少會有了個大概的了解。不過,還要指出的是這隻是一個很簡單的執行個體,如果要在實際中應用,則還需大大的改進,有興趣的讀者不妨一試。