给你一篇相关文章做个参考吧
Email 服务器的简单实现我们知道从Delphi 3 开始,它自带的控件中有基于Internet开发的控件。如果我们充分利用这些控件开发Internet程序则可以简化编程工作,提高效率。鉴于目前POP3客户端的软件的种类繁多,(如Outlook ExpressFoxmail 以及Web 方式下的各免费邮局)而服务器端(除Unix Email系统)很少公开原代码下面我就向大家着重介绍一下利用 Delphi 4中Internet控件组的TClientSocket 和TServerSocket 控件来实现 Email POP3服务器端。如果您理解了Email POP3服务器的构造,相信也可以依葫芦画瓢写出Email SMTP服务器程序。在此基础上加入多线程技术使服务器能同时处理多个客户的连接请求,您就可以轻松地实现一个简单的Email服务器了。
---- 一. 设计思路
---- Email 系统采用C/S 结构。当用户想发送邮件时或收取邮件时在客户机上运行任意一个客户端程序,如Foxmail。在菜单’工具->选项’的邮件服务器里填上运行我们服务器程序的主机名。服务器主机24小时一直运行我们的服务器端程序,SMTP和POP3服务器程序分别在25端口和110端口侦听连接请求。当用户发信时,首先客户端会与服务器端建立Socket连接。然后开始一应一答的Client/Server间的通信。发信和收信时建立连接后服务器端分别要发送一个’250 OK’ 和’+OK pop3 server is ready ’的应答。客户端收到此应答后开始发送SMTP或POP3命令。POP3通信时一般最开始的命令是’user ‘和’pass’或’ apop’用以进行身份验证。注意由于POP3会话有3个状态,某些命令只在某特定状态下有效。当用户进行完所有的操作后发送一个’quit’命令。服务器端收到此命令即终止此次socket连接并继续侦听其他的连接请求。注意:POP3通信时客户端在Transaction状态下’quit’则进入update状态。如果从Authorization状态下’quit’则终止通信,而不进入Update状态。如果客户端不通过’quit’命令终止连接,POP3会话不会进入Update状态。而只有在Update状态下收到’quit’命令后服务器才会在断连前把标志为已删的邮件进行物理删除。
---- 二. 代码实现(以POP3为例)
---- 自定义TPOP类的描述:
SessionState = ( InitAuthorizationTransactionUpdate);TPop=class (TComponent)publicUserName:string;//Email帐户名PassWord:string; //Email口令ReceText:Pchar; //server端收到的字符串PopState:SessionState;//pop状态:init or authorization or transaction or updateMsgCount:integer; //邮件总数SizeCount:integer; //邮件总大小ReplyString:string;//服务器端发送的应答信息DeleIndex:byte;//用户要删的邮件序号ListIndex:byte;//list方法 的参数:用户要列出的序号为listindex的邮件信息RetrIndex:byte;//retr方法的参数:用户要取序号为retrindex的邮件TopIndex:byte; //top方法的参数QuitFlag:boolean;//用户如果通过quit命断连则此变量为true;反之(此时要把f_dele都置回0)OldMsgCount:integer;//旧邮件数:Last 命令返回//邮件头的各个域HMsgId:string;HReplyTo:string;HDate:string;HFrom:string;HTo:string;HSubject:string;HMIME_Ver:real;HContent_Type:string;HContent_Transfer_Encoding:string;HText:string;//所有POP3服务器必须支持的命令procedure user;function pass:boolean;procedure stat;procedure dele;procedure list;procedure retr;procedure noop;procedure rset;procedure aquit;procedure tquit;//扩展的可选择实现的POP3 命令procedure top;procedure last;procedure apop;procedure uidl;end;---- 1. 建立连接
---- 我们可以看到利用了Tclientsocket后客户端请求建立连接只需下面的代码。
with ClientSocket dobeginHost := Server;Active := True;end;---- 服务器端利用TserverSocket一直在侦听110端口,若客户端有连接请求,则ServerSocketAccept事件会被激活,建立起连接。
procedure TMyForm.ServerSocketAccept(Sender: TObject;Socket: TCustomWinSocket);beginStatusbar1.Panels[0].Text :='连接到 ' + Socket.RemoteAddress;//pop对象初始化pop:=TPop.Create(nil);pop.PopState:=init;pop.LoginResult:=false;pop.QuitFlag:=false;ServerSocket.Socket.Connections[0].sendtext('+OK ibonc pop3 server is ready'+crlf);
end;---- 2. 通信
---- 服务器端收到客户端发来的信息,则会激活ServerSocketClientRead事件,通过ServerSocket的Socket.ReceiveText可以得到信息的内容。
procedure TMyForm.ServerSocketClientRead(Sender: TObject;Socket: TCustomWinSocket);var temp_command :string;//存放接收到的命令行,并做去crlf的处理begintemp_command:=Socket.ReceiveText;//to remove the crlf in command linetemp_command:=trim(copy(temp_command1
pos(crlftemp_command)-1));pop.ReceText:=pchar(temp_command);if pop.popstate=init thenif strLIComp(pop.ReceText'user '5)=0 thenpop.userelseServerSocket.Socket.Connections[0].sendtext('-ERR user name please')else if pop.popstate=authorization thenbeginif strLIComp(pop.ReceText'pass '5)=0 thenpop.passelse if strIComp(pop.ReceText'quit')=0 thenpop.aquitelseServerSocket.Socket.Connections[0].sendtext('-ERR pass word please');endelse if pop.popstate=transaction thenbeginif strIComp(pop.ReceText'stat')=0 thenpop.statelse if strLIComp(pop.ReceText'dele '5)=0 thenpop.deleelse if strLIComp(pop.ReceText'list'4)=0 thenpop.listelse if strLIComp(pop.ReceText'retr '5)=0 thenpop.retrelse if strIComp(pop.ReceText'noop')=0 thenpop.noopelse if strIComp(pop.ReceText'rset')=0 thenpop.rsetelse if strIComp(pop.ReceText'quit')=0 thenpop.tquitelse if strIComp(pop.ReceText'last')=0 thenpop.lastelse if strLIComp(pop.ReceText'apop '5)=0 thenpop.apopelse if strLIComp(pop.ReceText'uidl '5)=0 thenpop.uidlelseServerSocket.socket.connections[0].sendtext('-ERR no such command yet'+crlf);endend;---- 3. 关闭连接
procedure TMyForm.ServerSocketClientDisconnect(Sender: TObject;Socket: TCustomWinSocket);beginServerSocket.Active := False;//如果client端没有通过quit 命令断连,则在断连时要把那些f_dele置为0if pop.QuitFlag=False thenbeginMyForm.query11.Close;MyForm.query11.Params[0].Asstring:=pop.UserName;MyForm.query11.prepare;MyForm.query11.execsql;end;end;---- 三. 结语
---- 由于Email系统与数据库表结构的紧密联系笔者没有写出各POP3命令的具体实现。相信读者在认真阅读了RFC1939之后不难写出实现函数。现在就动手为你的公司写一个自己的Email服务器吧!