一个POP3客户端的C#类
作者: Bill Dean
关键词: C#, VB.NET,.NET, INTERNET
提交: 2002-02-26
更新: 2003-08-06
摘要: 本文描述的是一个实现了标准POP3命令的C#类。
· 下载VB.NET版本源文件(Ronny Reinertsen)
简介
读完Agus Kurniawan关于用C#与POP3服务器通讯的文章,我决定如果建立一个POP3客户端的类的话,我将走得更远。我决定为每一个标准的POP3命令实现方法,每一个方法返回一个包含(相应的)POP3服务器响应的字符串,某些情况命令无效,那么这些方法检查这些错误并且返回一个错误信息而不是直接发送命令到服务器。随文的源文件包含类的定义和一个使用该类与POP3邮件服务器通信的控制台演示程序(前面列出)。
类,方法和属性
随附的源代码包含一个类: POP3Client。
POP3Client类包含十个公用方法,其中九个代表标准的POP3命令:
· DELE,
· LIST,
· NOOP,
· PASS,
· QUIT,
· RETR,
· RSET,
· STAT
· and USER
每一个"POPmethods"返回一个字符串,内容为(相应的)POP3服务器响应结果,或者不恰当的命令导致的错误信息。最后一个公用方法:connect用来初始化连结。没有实现可选的POP3命令:APOP, TOP and UIDL。
开始工作后不久,我认识到所有POP3方法应该按以下四个基本步骤实现:
1. 检查该命令载当前的POP状态中是否有效。
2. 如果有效,发送命令到服务器。
3. 读取响应结果。
4. (如果需要)改变POP状态。
我决定用一个叫state的属性来跟踪POP会话的当前状态,数据类型声明为connect_state的枚举类型。你们可以看到这些声明在类的定义的顶部。
public enum connect_state {disc,AUTHORIZATION,TRANSACTION,UPDATE};
...
public connect_state state=connect_state.disc ;
有三个POP状态:AUTHORIZATION、TRANSACTION、UPDATE。(完整的POP3协议定义请参见RFC 1725)state属性在建立任何与POP3服务器的连接之前被设为disc(断开),建立连接后变为AUTHORIZATION。
I also thought it would be useful to store the user name, password, pop server name and error state (i.e.: did an error occur on the last POP3 command), so I add these properties to the class:
我也认为保存用户名、密码、POP3服务器名和错误状态是有用的(例如:判断上一个POP3命令是否发生错误),所以我在类中加入了这些属性:
public string user;
public string pwd;
public string pop;
public bool error;
连接服务器(TcpClient)、发送(NetworkStream)和接收数据(StreamReader)的机制直接取自Agus的代码。我甚至保留了相同的变量名:
// 借用自 Agus Kurniawan的文章:"Retrieve Mail From a POP3 Server Using C#"
// at http://www.codeproject.com/csharp/popapp.asp
private TcpClient Server;
private NetworkStream NetStrm;
private StreamReader RdStrm;
private string Data;
private byte[] szData;
private string CRLF = "\r\n";
在做任何其他工作之前,我们需要建立道服务器的连接以及取得网络流中的信息。这个在connect公用方法中进行。
public string connect()
{
// 初始化连接,这段代码是稍作修改“借” 来的
//来自Agus Kurniawan在www.codeproject.com的文章
// "Retrieve Mail From a POP3 Server Using C#"
// at http://www.codeproject.com/csharp/popapp.asp
// 用110端口创建服务器
Server = new TcpClient(pop,110);
try
{
// 初始化
NetStrm = Server.GetStream();
RdStrm= new StreamReader(Server.GetStream());
//POP会话现在被设为AUTHORIZATION状态
state=connect_state.AUTHORIZATION ;
return(RdStrm.ReadLine ());
}
catch(InvalidOperationException err)
{
return("Error: "+err.ToString());
}
}
注意:只要连接一初始化,会话就进入了AUTHORIZATION状态。
正如前面所提到的,九个POP3方法实质上具有相同的结构。DELE方法的源代码展示出该结构。
public string DELE(int msg_number)
{
string temp;
if (state != connect_state.TRANSACTION )
{
// 只有在POP会话处于TRANSACTION状态时 DELE 命令才为有效
temp="Connection state not = TRANSACTION";
}
else
{
issue_command("DELE " + msg_number.ToString ());
temp=read_single_line_response();
}
return(temp);
}
首先,我们知道DELE POP3命令只在TRANSACTION状态有效,所以如果该类不在这个状态,查看记录在state属性的值,那么返回一个错误信息而不是发送请求到POP3服务器。当处于别的状态时发送DELE命令也不会损伤POP3服务器,只是会产生内部的错误信息而已。如果你宁可让POP3服务器产生状态的错误信息,尽可移出if结构只留下issue_command()调用。一旦确定POP会话处于正确的状态,我们就使用私有方法issue_command发送"DELE"命令到服务器。RFC 1725描述了DELE命令返回一个从服务器发回的单行响应,我们用方法read_single_line_response来读取。两个方法都相当直接明了,不管是向建立连结的服务器写字符还是从建立连结的服务器读取来自于网络流的字符。因为执行完DELE命令后POP会话状态仍为TRANSACTION状态,我们没必要担心要改变state值。state改变的例子请参见源代码的PASS方法。
private void issue_command(string command)
{
// 发送命令到POP服务器,这段代码是稍作修改“借” 来的
// 来自Agus Kurniawan在www.codeproject.com的文章
// "Retrieve Mail From a POP3 Server Using C#"
// http://www.codeproject.com/csharp/popapp.asp
Data= command + CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
}
private string read_single_line_response()
{
// 读取POP服务器的响应结果,这段代码是稍作修改“借” 来的
// 来自Agus Kurniawan在www.codeproject.com的文章
// "Retrieve Mail From a POP3 Server Using C#"
// http://www.codeproject.com/csharp/popapp.asp
string temp;
try
{
temp = RdStrm.ReadLine();
was_pop_error(temp);
return(temp);
}
catch(InvalidOperationException err)
{
return("Error in read_single_line_response(): " + err.ToString ()) ;
}
}
注意,在某些情况,有些POP3命令返回多行响应。这里也采用一个名叫read_multi_line_response的方法来处理。我前面已经提到过,我认为显示地判断最后一个POP命令是否正确执行,或者POP3服务器是否返回错误,将是有用的。这里用was_pop_error私有方法来检查。
private void was_pop_error(string response)
{
// 检查POP服务器发出了响应并认为发生了错误
if(response.StartsWith ("-"))
{
// 如果第一个字符是"-"
// 那么pop服务器在执行客户端发过去的最后一个命令时发生错误
error=true;
}
else
{
// 成功
error=false;
}
}
使用示例
POP3协议的细节有点超出本文章的范围。所以如果你想多了解POP3的话,我建议你去RFC 1725,你可以在这里找到。让我们简单的看一下示例程序,以便了解怎样使用这个类:
static void Main(string[] args)
{
POP3Client.POP3client Demo = new POP3Client.POP3client();
Console.WriteLine ("****connecting to server:");
Console.WriteLine (Demo.connect ("your_pop3_server"));
Console.WriteLine ("****Issuing USER");
Console.WriteLine (Demo.USER ("user_id"));
Console.WriteLine ("****Issuing PASS");
Console.WriteLine (Demo.PASS ("password"));
Console.WriteLine ("****Issuing STAT");
Console.WriteLine (Demo.STAT () );
Console.WriteLine ("****Issuing LIST");
Console.WriteLine (Demo.LIST () );
Console.WriteLine ("****Issuing RETR 700...this will cause the POP3 server to gack a "
+ "hairball since there is no message 700");
Console.WriteLine (Demo.RETR (700) );
// 如果没有700消息,将会导致POP3抛出一个错误
Console.WriteLine ("****Issuing RETR 7");
Console.WriteLine (Demo.RETR (7) );
Console.WriteLine ("****Issuing QUIT");
Console.WriteLine (Demo.QUIT ());
Console.ReadLine ();
}
总之,你创建一个POP3Client类的实例,用你的POP3服务器调用connect方法,就可以开始发送命令了。你将需要调用USER和PASS方法以成功登陆服务器,调用其他POP3方法之前必须取得有效的登陆信息。
我希望你们部分人发觉这篇文章是有用的。我也希望收到反馈或建议。
请大家明白该代码没有任何的保证,不管是明示还是暗示。该代码严格的“无保证”提供,仅仅为教育目的。谁使用谁负责。要使用该代码,请允诺使用该代码带来的任何损失与作者和Restek无关。
历史
2003-08-06 —— Ronny Reinertsen好意地把C#版本转为VB.NET
(完)