技术文章 > 信箱监视程序

信箱监视程序

2018-10-20 00:31

文档管理软件,文档管理系统,知识管理系统,档案管理系统的技术资料:
----本 文 将 向 大 家 介 绍 怎 样 编 写 自 己 的 信 箱 监 视 程 序, 程 序 将 直 接 调 用WinSock 函 数 来 进 行 网 络 通 信。 除 了 具 备WinSock 编 程 知 识 之 外, 还 必 须 了 解POP3 协 议。 下 面 是 对POP3 的 一 个 粗 略 的 介 绍, 读 者 可 以 参 看RFC 1225 更 为 详 细 地 了 解 该 协 议。
一、 关 于POP3 协 议
----POP3 服 务 器 程 序 通 常 在TCP 端 口110 提 供 服 务。 当 客 户 想 要 使 用 服 务 时, 它 便 与 服 务 器 建 立 一 个TCP 连 接。 一 旦 连 接 建 立,POP3 服 务 器 就 向 客 户 发 送 一 条 欢 迎 消 息。 然 后 客 户 开 始 给 服 务 器 发 送 命 令, 服 务 器 则 给 出 相 应 的 回 答。POP3 的 命 令 由 一 个 关 键 词 或 者 关 键 词 加 参 数 组 成。 每 个 命 令 以 回 车 换 行(0xD0xA) 作 为 结 束 标 志。 对 于 所 有 的 命 令,POP3 服 务 器 都 会 提 供 一 个 回 答。 服 务 器 的 回 答 由 一 个 状 态 标 志 加 一 些 附 加 信 息 组 成。 目 前 使 用 的 两 个 标 志 是“ +OK” 和“ -ERR”, 分 别 表 示 客 户 的 命 令 是 否 合 法。 所 有 的 回 答 也 是 以 回 车 换 行 结 束。
----与 本 文 讨 论 的 话 题 相 关 的 四 个POP3 命 令 是USER、PASS、LIST 和QUIT。
USER 命 令
格 式USER name
----其 中name 是 用 户 在 该POP3 服 务 器 上 的 用 户 标 识。 客 户 应 该 在 接 到 服 务 器 的 欢 迎 消 息 后 或 者 在 上 一 个 USER 或 者PASS 失 败 之 后 可 以 发 送 此 命 令。
PASS 命 令
格 式PASS string
----其 中string 为 该 用 户 的 密 码。 客 户 在 发 送 了USER 命 令 并 且 收 到 了 +OK 的 回 答 之 后 方 可 发 送 此 命 令。 如 果 用 户 名 和 密 码 都 正 确, 服 务 器 回 答 +OK, 否 则 -ERR。
LIST 命 令
格 式LIST
----如 果 该 用 户 有 邮 件, 则LIST 命 令 会 回 答 +OK, 并 列 出 所 有 邮 件 的 标 识 符 和 大 小( 每 个 邮 件 一 行), 最 后 一 个 仅 包 含 一 个 句 点 的 行(0xD0xA0x2E) 表 示 整 个 回 答 的 结 束。 如 果 该 用 户 没 有 邮 件, 有 些 服 务 器 会 返 回 -ERR, 有 些 在 可 能 返 回 一 个 +OK 和 一 个 仅 包 含 一 个 句 点 的 行。 当 然, 客 户 必 须 在PASS 命 令 通 过 之 后 客 户 程 序 才 能 给 服 务 器 发 送LIST 命 令。
QUIT 命 令
----从POP3 服 务 器 上 退 出 登 录。
二、 实 现 相 关 函 数
----接 下 来 我 们 按 照POP3 协 议 所 定 义 的 通 信 规 则 来 实 现 一 个 名 叫POP3CheckMail 的 函 数, 只 要 调 用 此 函 数, 我 们 就 可 以 检 测 信 箱 了。
----下 面 的 代 码 是 用 与Delphi 4 兼 容 的Pascal 语 言 实 现 的, 我 们 必 须 包 含WinSock 单 元, 并 且 在 调 用 下 列 函 数 之 前 初 始 化 好WinSock 动 态 连 接 库。 初 始 化WinSock 动 态 连 接 库 的 代 码 如 下:
----if WSAStartup( $002, wsadata)<>0 then Halt;
----POP3CheckMail 的 原 型 如 下:
----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 指 定 端 口 号。
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(‘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 左 右。