序
因工作需要将QQ域名邮箱中的邮件列表、内容读取出来,所以研究了下这2个东东。
在开始做这个东西前,首先我们要了解下POP3协议是什么、与SMTP和IMTP有什么区别、能实现什么功能、操作的差异是怎样的。POP3协议所能提供的指令有限,并不提供时间查询、关键字查询、未读邮件查询这类功能。所以需要搭配本地存储环境来实现邮箱的常规功能。
我们先来了解下POP3协议。
POP3协议
POP3邮件服务器通过侦听TCP端口110提供POP3服务。客户端软件读取邮件之前,需要事先与服务器建立TCP连接。连接成功后,POP3服务器会向该客户端发送确认消息。然后客户端根据服务器回送的信息决定下一步的操作。
客户端每次向POP3服务器发送命令后,都要等待服务器响应,并处理接收的信息,然后再接着发送下一个命令,如此往复多次,一直持续到连接终止。这个过程经历了三个状态:授权(AUTHORIZATION)状态、操作(TRANSACTION)状态和更新(UPDATE)状态。
POP3服务器回送的响应信息由一个状态码和一个可能跟有附加信息的命令组成。所有响应也以回车换行结束(发送命令也是)。状态码有两种:“确定”(“+OK”)和“失败”(“-ERR”)。对于客户端发送的每一条命令,服务器都会回送状态码。因此在客户端程序中,可以通过服务器回送的状态码对应的字符,即判断第一个字符是“+”号还是“-”号来确定服务器是否正确响应客户端发送的命令。
指令大全
客户端首先与POP3服务器建立TCP连接,服务器接收后发送一个单行的确认信息。例如“+OK POP3 server ready”,此时POP3会话就进入了授权状态。在授权状态,客户需要向服务器发送用户名和密码进行确认。
* 假设用C表示客户端(Client),S表示服务器端(Server),下面是客户端接收邮件前需要与服务器传输的信息。
1) 发送用户名:
语法形式:USER <用户名>
功能:将客户的用户名发送到服务器。
服务器返回:+OK正确的用户名;-ERR错误的用户名。
示例:C:USER myname@126.com
S:+OK welcome on this server.
上述两行代码的含义为:客户端发送“USER myname@126.com”,服务器端回送信息“+OK welcome on this server.”。
2) 用户名确认成功后,需要输入密码:
语法形式:PASS <密码>
功能:将客户的密码发送给服务器。
服务器返回:+OK正确的用户名;-OK错误的用户名。
示例:C:PASS *****
S:+OK myname logged in at 19:04
授权成功后,进入操作状态。
(1) STAT命令
语法形式:STAT
功能:从服务器中获取邮件总数和总字节数。
服务器返回:邮件总数和总字节数。
示例:C:STAT
S:+OK 2 320
语法形式:LIST
功能:从服务中获得邮件列表和大小。
服务器返回:列出邮件列表和大小。
示例:C:LIST(LIST命令针对pop3邮箱会首先返回+ok 邮件总数 邮件总大小,但对于pop邮箱则只返回+ok状态字符,因此最好还是通过STAT来判断邮件总数)
S:+OK 2 messages (320 octets)
S:1 120
S:2 200
S: .
语法形式:RETR <邮件的序号>
功能:从服务器中获得一个邮件。
服务器返回:+OK成功;-ERR错误。
示例:C: RETR 1
S:+OK 120 octets
S:<服务器发送信件1内容>
S: .
注意,这里的“.”是单独发送的。
语法形式:DELE <邮件的序号>
功能:服务器将邮件标记为删除,当执行QUIT命令时才真正删除。(可以通过RSET 命令进行撤消删除 )
服务器返回:+OK成功;-ERR错误。
示例:C:DELE 1
S:+OK 1 Deleted
当客户发送QUIT命令时,会话进入更新状态。
当客户在操作状态下发送QUIT命令后,会话进入更新状态。
QUIT命令
语法形式:QUIT
功能:关闭与服务器的连接。
服务器返回:+OK;-ERR。
示例:C:QUIT
S:+OK
然后服务器自动断开与该客户端的TCP连接。
通过此命令来返回对应邮件的唯一号,然后在本地判断是否已读或未读。
uidl命令
格式:uidl [n] 参数n可选,n为邮件编号uidl命令,与list同,不过显示邮件的信息比list更详细,更具体
原理理解和实现
为什么我获得的邮件都是未读的?
因为POP指令中的LIST指令不提供查询功能,所以不能实现“获得未读列表”、“根据日期查询”、“获得已删除列表”等等功能。
这也是为什么用OutLook、FoxMail这类客户端工具在第一次“收取”到的邮件,即使服务器上(以QQ邮箱为例)该邮件的状态已经是“已读”,但客户端工具还是显示为未读新邮件的道理一样。邮件本身不具备这些属性,这些属性是由客户端工具追加上去的扩展属性(这样做的好处是便于终端用户的使用和管理),并且保存在客户端工具同级的环境中。
那我应该怎么实现未读和已读以及删除列表这类功能呢?
若要用PHP实现邮箱的这些常规功能,则需要搭配MYSQL或其他存储方式来存储POP服务器初次返回的数据,以便后期判断是否有新邮件。
POP服务器返回的数据:邮件总数量、邮件信息(发件人、内容、标题)。再根据本地保存的数据与服务器上的最新数据进行对比,是否有新邮件,有的话则通过LIST指令获得新邮件的指标(ID),通过RERT指令获取该邮件内容。
客户端类工具的实现流程基本是一样的,这是我根据FoxMail的实现效果推理出的流程图:
PHP的代码应该如何实现呢?
通过socket()建立接口连接:
// Connect to the POP3 server $this->pop_conn = fsockopen($host, // POP3 Host $port, // Port # $errno, // Error Number $errstr, // Error Message $tval); // Timeout (seconds)
发送请求(通过fwrite()可以实现发送请求的目的,如果填写了filesize参数,则立即发送请求;具体看手册!):
$string = "STAT \r\n"; $bytes_sent = fwrite($this->pop_conn, $string, strlen($string));
解读请求:
$pop3_response = fgets($this->pop_conn, 128); if (substr($pop3_response, 0, 3) !== '+OK') { $this->error = array( 'error' => "Server reported an error: $string", 'errno' => 0, 'errstr' => '' ); return false; } else { return true; //或者继续用$pop3_response处理其他事情 }
发表回复