您的位置:首页 > 编程语言

了解POP3协议,使用简单的代码监控pop3邮箱,或者不用代码,直接使用telnet

2008-01-22 16:17 991 查看
POP3协议要点 ,更详细的信息可以查看 RFC1939
(1) POP3命令由一个命令和一些参数组成。所有命令以一个CRLF对结束
(2) 命令和参数由可打印的ASCII字符组成,它们之间由空格间隔
(3) POP3响应由一个状态码和一个可能跟有附加信息的命令组成。所有响应也是由CRLF对结束
(4) 有两种状态码,"确定" ("+OK")和"失败" ("-ERR")。
(5) 当所有信息发送结束时,发送最后一行,包括一个结束字符(十进制码46,也就是".")和一个CRLF对。
(6) 在POP3协议中有三种状态,认可状态,处理状态,和更新状态
  当客户机与服务器建立联系时,一旦客户机提供了自己身份并成功确认,即由认可状态转入处理状态,在完成相应的操作后客户机发出QUIT命令,则进入更新状态,更新之后最后重返认可状态。如下图

 等待连接        身份确认         QUIT命令
   ——  |认可|————— |处理|——————|更新|
           |__________________________________|
                  重返认可状态

一个最小的,成功的Client-Server会话过程

POP3 Client POP3 Server
Socket() listen()

Connect() -------------------------------> accept()
<------------------------------ +OK 发送确认消息 (进入"确认"状态)

发送USER命令------------------------------>
<----------------------------- +OK 发送确认消息
发送PASS命令------------------------------>
<----------------------------- +OK 发送确认消息 (进入"操作"状态)

发送QUIT命令------------------------------>
<----------------------------- +OK 发送确认消息 (进入"更新"状态)
如果客户在"确认"状态下发送QUIT后,会话并不进入"更新"状态

可以在命令行(cmd.exe)里测试一下:
c:/telnet pop3.163.com 110 //连接网易的邮件服务器
+OK coremail .... //服务器返回+OK
USER 'yourusername' //客户端发出用户名,可能在yourusername上需要一对单引号
PASS 'yourpassword' //客户端发出密码,可能在yourpassword上需要一对单引号
+OK mm nn // 返回邮件数和大小
STAT //得到邮箱的状态
+OK xxx xxx // 返回邮件数和大小

客户可以发出的命令列表以及含义,源自ChinaUnix

 命令  参数    状态     描述
------------------------------------------------------------------------------------------------------------------------------
USER username    认可  此命令与下面的pass命令若成功,将导致状态转换
PASS password    认可     
APOP Name,Digest  认可  Digest是MD5消息摘要,Windows系统多不支持
------------------------------------------ -----------------------------------------------------------------------------------
STAT None     处理 请求服务器发回关于邮箱的统计资料,如邮件总数和总字节数
UIDL [Msg#]     处理 返回邮件的唯一标识符,POP3会话的每个标识符都将是唯一的
LIST [Msg#]     处理  返回邮件数量和每个邮件的大小
RETR [Msg#]     处理  返回由参数标识的邮件的全部文本
DELE [Msg#]     处理  服务器将由参数标识的邮件标记为删除,由quit命令执行
RSET None      处理  服务器将重置所有标记为删除的邮件,用于撤消DELE命令
TOP [Msg#]     处理  服务器将返回由参数标识的邮件前n行内容,n必须是正整数
NOOP None     处理  服务器返回一个肯定的响应
------------------------------------------------------------------------------------------------------------------------------
QUIT None     更新 

Foxmail 和 Outlook 使用 pop3时的异同 源自LinuxForum.net
假定服务器上有三封邮件等待客户机接收。用foxmail与OE的不同之处在于foxmail每收一封标记删除一封,而Outlook则等全部接收完后再全部标记为删除最后执行quit命令。
Foxmail    OutLook
-------------------------------------------
RETR 1 RETR 1
DELE 1 RETR 2
RETR 2 RETR 3
DELE 2 DELE 1
RETR 3 DELE 2
DELE 3 DELE 3
QUIT QUIT
-------------------------------------------     
简单的C#代码就可以实现pop3邮箱的监控
namespace Sky.MailMonitor






{




public class Pop3






{


private const int bufsize = 1024; //default 1kbytes buffers




//服务器确认消息的状态码,必须是大写的


private const string OKFlag = "+OK";


private const string ERRFlag = "-ERR";


private int _port = 110;


public int Port






{


get






{


return _port;


}




set






{


_port = value;


}


}


private int mailCount = 0;


public int MailCount






{


get






{


return mailCount;


}


set






{


mailCount = value;


}


}




private int mailSpace = 0;


public int MailSpace






{


get






{


return mailSpace;


}


set






{


mailSpace = value;


}


}




private Socket socket = null;


public int port






{




get

{ return Port; }


}




//step 1: 连接一个POP3服务器,希望得到+OK的确认消息


public void Connect(string hostname)






{


//建立一个套接字


socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);




IPHostEntry hostInfo = Dns.GetHostByName(hostname);


IPEndPoint endPoint = new IPEndPoint(hostInfo.AddressList[0], Port);




socket.Connect(endPoint);


string tmp = RecvLine(); //blocking,wait for '+OK'


Console.WriteLine(tmp);


}


//step 2:发送命令字USER和PASS


public void Login(string username,string password)






{




SendCommand("USER " + username);


string tmp=RecvLine();


Console.WriteLine("Server say: {0}", tmp);




SendCommand("PASS " + password);


string tmp1=RecvLine();


Console.WriteLine("Server say: {0}", tmp1);


}


//step 3:发送命令字STAT,请求服务器发回关于邮箱的统计资料,如邮件总数和总字节数


//C:STAT


//S:+OK nn mm


//nn是邮件数量,mm是大小


public void Stat()






{


SendCommand("STAT");


string tmp=RecvLine();


tmp = tmp.Substring(tmp.IndexOf(" ") + 1); //在+OK和一个空格之后得到邮件数量,字节数


string tmp1 = tmp.Substring(0, tmp.IndexOf(" ")).Trim();


MailCount=Int32.Parse(tmp.Substring(0, tmp.IndexOf(" ")).Trim());


Console.WriteLine("Server say: {0}", tmp);


// MailSpace = Int32.Parse(tmp1.Substring(0, tmp1.IndexOf(" ")).Trim());


}


// LIST [msg] ,msg参数是可选的, 返回邮件数量和每个邮件的大小,如果没有msg参数,服务器将返回所有的邮件信息


// C:LIST


// S:+OK 187 9703827


//1 31502


//2 1168


//3 1826


//.

.


public void List()






{


SendCommand("LIST");


string tmp = RecvLine();


Console.WriteLine("Server say: {0}", tmp);


}






public void Dele(string mailNumber)






{


SendCommand("DELE " + mailNumber);


string tmp = RecvLine();


Console.WriteLine("Server say: {0}", tmp);


}




public void Quit()






{


SendCommand("QUIT");


string tmp = RecvLine();


Console.WriteLine("Server say: {0}", tmp);


}




//


//关闭打开的连接并且发送QUIT命令字,QUIT命令没有参数


public void Close()






{


if (socket == null)


return;


try






{


Quit(); //first invoke Quit method


}


finally






{


socket.Close();


}


}


private void SendCommand(string command)






{


command += " ";//必须在命令字后添加CRLF对


byte[] buffer = Encoding.ASCII.GetBytes(command.ToCharArray());




// 发送缓冲


int bytesSent = socket.Send(buffer, buffer.Length, 0);




if (bytesSent != buffer.Length)


throw new Exception("failed to send request to server");


}


// TODO :服务器以Multi-line应答


private string RecvMultiLine()






{


}


// 服务器以line+CRLF形式应答


private string RecvLine()






{


//需要添加一个空的终结符


byte[] buffer = new byte[bufsize + 1];


StringBuilder message = new StringBuilder(bufsize);


int bytesRead;


// 读取缓冲区


for (; ; )






{


bytesRead = socket.Receive(buffer, bufsize, SocketFlags.None);


//读取完毕


if (bytesRead == 0)


break;


buffer[bytesRead] = 0;




// conver char array to string


message.Append(Encoding.ASCII.GetChars(buffer, 0, bytesRead));




if (buffer[bytesRead - 1] == 10) //=LF #hex:0A #Dec:10


break;




}


string tmp = message.ToString();


if (tmp.StartsWith(OKFlag) == false) //服务器消息不是以+OK起始的确认消息






{


throw (new Exception(tmp));


}


return tmp;


}


}


}
上面的代码完成了监控的基本功能,但是还不够,首先缺少异常处理,比如在socket.connect() 那里,连接失败了怎么办?其次,如果你想把邮件收到本地,那么字符的编码就成了问题,尤其是汉字的处理比较麻烦,详细的情况可以查看 RFC822 .你可以完善基本的代码,实现本地POP3收信了.
现在可以测试一下:


Using directives#region Using directives




using System;


using System.Collections.Generic;


using System.Text;




#endregion




namespace Sky.MailMonitor






{


class Program






{


static void Main(string[] args)






{


Pop3 pop3 = new Pop3();


string username="xxxxxx";


string password="xxxxxx";


string hostname="pop3.163.com";


try






{


pop3.Connect(hostname);


pop3.Login(username, password);


pop3.Stat();


pop3.List();


}


catch(Exception e)






{


Console.WriteLine(e.Message);


}


finally






{


pop3.Close();


}






}


}


}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐