本文将向大家介绍怎样编写自己的信箱监视程序,程序将直接调用WinSock函数来进行网络通信。除了具备WinSock编程知识之外,还必须了解POP3协议。下面是对POP3的一个粗略的介绍,读者可以参看RFC1225更为详细地了解该协议。
一、关于POP3协议
POP3服务器程序通常在TCP端口110提供服务。当客户想要使用服务时,它便与服务器建立一个TCP连接。一旦连接建立,POP3服务器就向客户发送一条欢迎消息。然后客户开始给服务器发送命令,服务器则给出相应的回答。POP3的命令由一个关键词或者关键词加参数组成。每个命令以回车换行(0xD0xA)作为结束标志。对于所有的命令,POP3服务器都会提供一个回答。服务器的回答由一个状态标志加一些附加信息组成。目前使用的两个标志是“+OK”和“-ERR”,分别表示客户的命令是否合法。所有的回答也是以回车换行结束。
与本文讨论的话题相关的四个POP3命令是USER、PASS、LIST和QUIT。
USER命令
格式USERname
其中name是用户在该POP3服务器上的用户标识。客户应该在接到服务器的欢迎消息后或者在上一个USER或者PASS失败之后可以发送此命令。
PASS命令
格式PASSstring
其中string为该用户的密码。客户在发送了USER命令并且收到了+OK的回答之后方可发送此命令。如果用户名和密码都正确,服务器回答+OK,否则-ERR。
LIST命令
格式LIST
如果该用户有邮件,则LIST命令会回答+OK,并列出所有邮件的标识符和大小(每个邮件一行),最后一个仅包含一个句点的行(0xD0xA0x2E)表示整个回答的结束。如果该用户没有邮件,有些服务器会返回-ERR,有些在可能返回一个+OK和一个仅包含一个句点的行。当然,客户必须在PASS命令通过之后客户程序才能给服务器发送LIST命令。
QUIT命令
从POP3服务器上退出登录。
二、实现相关函数
接下来我们按照POP3协议所定义的通信规则来实现一个名叫POP3CheckMail的函数,只要调用此函数,我们就可以检测信箱了。
下面的代码是用与Delphi4兼容的Pascal语言实现的,我们必须包含WinSock单元,并且在调用下列函数之前初始化好WinSock动态连接库。初始化WinSock动态连接库的代码如下:
ifWSAStartup($002,wsadata)<>0thenHalt;
POP3CheckMail的原型如下:
1 |
function POP3CheckMail(Email,Password:String;var MailList:TStringList;var ErrorMsg:String):Bool; |
参 数 说 明:
Email 和Password 分别为用户的email信箱名和口令。
变量参数MailList用于返回邮件的标识和大小,MailList.Count表示邮件的封数。
变量参数ErrorMsg返回出错消息。
以下是POP3CheckMail及其它所用到的函数的实现代码。
Connect_Server函数功能:与指定的主机建立一个TCP连接,返回一个Socket描述符。参数host指定主机的名字,Port指定端口号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
function Connect_Server(host:string;Port:integer):integer; var i:integer; p:^LongInt; phe:pHostEnt; sin:sockaddr_in; begin sin.sin_family:=AF_INET; sin.sin_port:=htons(Port); //Get the IP for host, allowing for dotted decimal phe:=gethostbyname(pchar(host)); if phe<>nil then begin p:=Pointer(phe^.h_addr_list^); sin.sin_addr.s_addr:=p^; end else begin i:=inet_addr(PChar(Host)); if i<> -1 then sin.sin_addr.S_addr:=i end; //create a socket Result:=socket(PF_INET,SOCK_STREAM,0); if (Result=INVALID_SOCKET) then Exit; //connect to server if Connect(Result,sin,sizeof(sin))=SOCKET_ERROR then begin {Error handling} end; end; |
Write_Socket 函 数
功 能: 向Socket写入一个字符串。
1 2 3 4 |
function Write_Socket(sockfd:Integer; const s:string):Integer; begin result:=Winsock.Send(sockfd,pointer(s)^,Length(s),0) end; |
Socket_Readline 函 数
功 能: 从Socket上读取一行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Socket_Readline(sockfd:Integer):String; //Read until #10 var S:String; buf:array[0..1]of Char; n:Cardinal; begin buf[0]:= #0;buf[1]:= #0; S:=‘'; n:=recv(sockfd,Buf,1,0); while n>0 do begin buf[1]:= #0; S:=S + buf; if (buf[0]= #10) then Break; n:=recv(sockfd, buf, 1, 0); end; Result:=Trim(S); end; |
Pop3Response 函 数
功 能:读取POP3服务器的一行返回信息,如果是“+OK” 则函数返回TURE,如果是“-ERR”则返回FALSE。
1 2 3 4 5 6 7 |
function Pop3Response(Sockfd:Integer):Bool; var S: string; begin S:=socket_readline(sockfd); if copy(s,1,3)=‘ +OK' then Result:=True else {if copy(s,1,4)=‘ -ERR' then }Result:=False; end; |
POP3CheckMail 函 数
功 能:检测名字为email的信箱,如果有新邮件,则通 过变量参数MailList将每一封邮件的大小返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
function POP3CheckMail (Email,Password:String;var MailList: TStringList;var ErrorMsg:String):Bool; var sockfd,i:integer; S, Host, User:String; begin Result:=False; ErrorMsg:=‘'; if MailList=nil then Exit; S:=Trim(Email); i:=Pos(‘@',Email); User:=Trim(Copy(S,1,i -1)); Host:=Trim(Copy(S,i +1,Length(Email) -i)); MailList.Clear; if (user=‘')or(host=‘') then begin ErrorMsg:=‘Invalid email address.';exit; end; if (Host[1]=‘[')and (Host[Length(host)]=‘]') then begin Host[1]:=‘ ';Host[Length(host)]:= #0;end; Host:=Trim(host); sockfd:=Connect_Server(Host,110); if not Pop3Response(sockfd)then begin ErrorMsg:= ‘Cannot connect to server';exit; end; Write_Socket(sockfd,‘USER ' +User + #13 #10); IF NOT POP3Response(sockfd) then begin ErrorMsg:= ‘USER failed'; Exit;end; Write_Socket(sockfd,‘PASS ' +Password + #13 #10); IF NOT POP3Response(sockfd) then begin ErrorMsg:= ‘PASS failed'; Exit;end; Write_Socket(sockfd,‘LIST' #13 #10); POP3Response(sockfd); while true do begin s:=Socket_readline(sockfd); if s=‘.' then BREAK; MailList.Add(S); end; Write_Socket(sockfd,‘QUIT' #13 #10); Closesocket(sockfd); Result:=True; end; |
三、 邮件的检测?
下面我们来看一个使用POP3CheckMail函数的简单示例。
1 2 3 4 5 6 7 8 9 10 11 12 |
var MailList:TstringList; ErrorMsg:String; ... MailList:=TstringList.Create; POP3CheckMail(‘simon_liu@263.net', ‘mypassword', MailList, ErrorMsg); If MailList.Count>0 then MessageBox(0, Pchar(‘You have ' +IntToStr (MailList.Count) + ‘ new messages!'), ‘New Message!', MB_ICONINFORMATION) Else if ErrorMsg=‘' then MessageBox (0, ‘No message!', ‘',0) Else MessageBox(0, Pchar(ErrorMsg), ‘Error', 0); MailList.Free; |
如果你仔细阅读了POP3CheckMail函数的实现代码,你会发现此函数除了可以获取邮件的封数之外,还可以获得每一封邮件的大小。你可以通过POP3CheckMail函数的变量参数MailList的Strings数组来获取邮件的大小。
实现了POP3CheckMail函数,再在此基础上编写一个POP3信箱的监视程序就变得很简单了。你可以通过一个定时器来定期地调用POP3CheckMail函数,这样你就可以监视某个email信箱了。假若你想要同时监视多个email信箱,只要为每一个信箱创建一个线程并且在线程中定期调用POP3CheckMail函数即可。你的程序中如果没有使用Delphi的控件,那么一个完整的信箱监视程序可能只有60K左右。