最近做的项目需要下载邮件并且进行分析,因此需要有一个能访问POP3服务器并取得邮件的程序。在网上找了下相关的程序,一般都是采用TcpClient的方法,连接到POP3服务器后,通过流发送相应的命令完成与服务器的互动,相关的命令和流程大致如下:
Client : +OK Server POP Ready!!
Client : USER xxx
Server : +OK
Client : PASS yyy
Server : +OK user successfully logged on
Client : STAT
Server : +OK n m
Client : RETR 1
Server : +OK
---{ data }-----
Client : QUIT
Server : +OK Server POP signing off
程序内容大致如下:
声明公共变量:
- public TcpClient Server;
- public NetworkStream NetStrm;
- public StreamReader RdStrm;
- public string Data;
- public byte[] szData;
- public string CRLF = "/r/n";
下面是连接POP3服务器的代码:
- private void ConnectBtn_Click(object sender, System.EventArgs e)
- {
- // change cursor into wait cursor
- Cursor cr = Cursor.Current;
- Cursor.Current = Cursors.WaitCursor;
- // create server POP3 with port 110
- Server = new TcpClient(POPServ.Text,110);
- Status.Items.Clear();
- try
- {
- // initialization
- NetStrm = Server.GetStream();
- RdStrm= new StreamReader(Server.GetStream());
- Status.Items.Add(RdStrm.ReadLine());
- // Login Process
- Data = "USER "+ User.Text+CRLF;
- szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
- NetStrm.Write(szData,0,szData.Length);
- Status.Items.Add(RdStrm.ReadLine());
- Data = "PASS "+ Passw.Text+CRLF;
- szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
- NetStrm.Write(szData,0,szData.Length);
- Status.Items.Add(RdStrm.ReadLine());
- // Send STAT command to get information ie: number of mail and size
- Data = "STAT"+CRLF;
- szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
- NetStrm.Write(szData,0,szData.Length);
- Status.Items.Add(RdStrm.ReadLine());
- // change enabled - disabled button
- ConnectBtn.Enabled = false;
- DisconnectBtn.Enabled = true;
- RetrieveBtn.Enabled = true;
- // back to normal cursor
- Cursor.Current = cr;
- }
- catch(InvalidOperationException err)
- {
- Status.Items.Add("Error: "+err.ToString());
- }
- }
下面是断开服务器的代码:
- private void DisconnectBtn_Click(object sender, System.EventArgs e)
- {
- // change cursor into wait cursor
- Cursor cr = Cursor.Current;
- Cursor.Current = Cursors.WaitCursor;
- // Send QUIT command to close session from POP server
- Data = "QUIT"+CRLF;
- szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
- NetStrm.Write(szData,0,szData.Length);
- Status.Items.Add(RdStrm.ReadLine());
- //close connection
- NetStrm.Close();
- RdStrm.Close();
- // change enabled - disabled button
- ConnectBtn.Enabled = true;
- DisconnectBtn.Enabled = false;
- RetrieveBtn.Enabled = false;
- // back to normal cursor
- Cursor.Current = cr;
- }
以下是取得某封特定邮件内容的代码:
- private void RetrieveBtn_Click(object sender, System.EventArgs e)
- {
- // change cursor into wait cursor
- Cursor cr = Cursor.Current;
- Cursor.Current = Cursors.WaitCursor;
- string szTemp;
- Message.Clear();
- try
- {
- // retrieve mail with number mail parameter
- Data = "RETR "+ Number.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();
- }
- }
- else
- {
- Status.Items.Add(szTemp);
- }
- // back to normal cursor
- Cursor.Current = cr;
- }
- catch(InvalidOperationException err)
- {
- Status.Items.Add("Error: "+err.ToString());
- }
- }
这样的过程存在一些缺点:1。程序实现复杂;2。需要使用流从服务器将数据完全下载下来后才能对邮件内容进行解析,这样如果邮件很大或者邮件数量过多的话需要耗费大量的时间。
在《RSS and Atom in Action》一书里面有说到一个.net下的邮件处理程序集Lesnikowski,可以简化以上的过程。但是在使用《RSS and Atom in Action》书的网站上提供的那个Lesnikowski.Pawel.Mail.Pop3.dll时发现可以解析来自tom.com的邮件,但是在解析来自公司邮箱的邮件时居然找不到邮件的Body...而且还存在一个问题,这个程序集中的函数不能自动对Base64编码过的邮件进行解码。现在Lesnikowski提供了新的版本,试验了下解决了Body为null的问题,而且可以自动对Base64进行解码。但是新的版本需要购买,否则一些功能用不了,而且会是不是在邮件正文里面加一句“请购买。。。。。”的提示。不过这样还好,反正用不了的功能我也不需要,我只需要用到它提供的两个属性:TextDataString和HtmlDataString还是可以用的,一个会忽略掉HTML标签只返回文本,一个会返回带有HTML标签的文本,还省去了我解析格式化的邮件正文的麻烦。至于时不时加入一句提醒购买的话也是可以解决掉的问题,因此可以暂时先凑合着用 - - 。以下是官方上的一个使用Lesnikowski连接POP3服务器的例子:
- using Lesnikowski.Client;
- using Lesnikowski.Mail;
- using Lesnikowski.Mail.Headers;
- using Lesnikowski.Mail.Headers.Constants;
- using System;
- class Program
- {
- static void Main(string[] args)
- {
- Pop3 pop3 = new Pop3();
- pop3.User = "lesnikowski"; // Set user name and password
- pop3.Password = "password";
- try
- {
- pop3.Connect("mail.host.com"); // Connect to server and login
- pop3.Login();
- pop3.GetAccountStat(); // Get account statistics
- SimpleMailMessageBuilder builder = new SimpleMailMessageBuilder();
- for(int i=1; i<=pop3.MessageCount; i++)
- {
- try
- {
- // Receive mail
- ISimpleMailMessage simpleMail = builder.CreateFromEml(
- pop3.GetMessage(i)
- );
- // Write out received mail message
- Console.WriteLine( "-- email {0} --", i);
- Console.WriteLine( simpleMail.Subject );
- Console.WriteLine( simpleMail.TextDataString );
- // Use other properties to access mail data
- }
- catch(MailException ex)
- {
- Console.WriteLine( ex.Message ); // Invalid mail format
- }
- }
- }
- catch(ServerException ex)
- {
- Console.WriteLine( ex.Message ); // Server error
- }
- finally
- {
- pop3.Close(false); // Close connection
- }
- Console.WriteLine( "Receive end./n Press enter..." );
- Console.ReadLine();
- }
- };
下面的网站上提供了老的dll的实现方式,基本也是按照最上面的通过流的方式来实现的,对于了解这个程序集的后台实现原理比较有帮助。
https://dasblogce.svn.sourceforge.net/svnroot/dasblogce/trunk/source/Lesnikowski.Pawel.Mail.Pop3/