本文將向大家介紹怎樣編寫自己的信箱監視程序,程序將直接調用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的原型如下:
functionPOP3CheckMail(Email,Password:String;varMailList:TStringList;varErrorMsg:String):Bool;
參數說明:
Email和Password分別為用戶的email信箱名和口令。
變量參數MailList用於返回郵件的標識和大小,MailList.Count表示郵件的封數。
變量參數ErrorMsg返回出錯消息。
以下是POP3CheckMail及其它所用到的函數的實現代碼。
Connect_Server函數
功能:與指定的主機建立一個TCP連接,返回一個Socket描述符。參數host指定主機的名字,Port指定端口號。
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寫入一個字符串。
function Write_Socket(sockfd:Integer; const s:string):Integer;
begin
result:=Winsock.Send(sockfd,pointer(s)^,Length(s),0)
end;
Socket_Readline函數
功能:從Socket上讀取一行。
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。
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將每一封郵件的大小返回。
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函數的簡單示例。
var MailList:TstringList;
ErrorMsg:String;
...
MailList:=TstringList.Create;
POP3CheckMail(‘[email protected]',
‘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左右。