Email 服务器的简单实现
点击次数:53 次 发布日期:2008-11-09 08:39:48 作者:源代码网
|
源代码网推荐 源代码网推荐 一. 设计思路 源代码网推荐 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’命令后服务器才会在断连前把标志为已删的邮件进行物理删除。 软件开发网 www.mscto.com 源代码网推荐 二. 代码实现(以POP3为例) 源代码网推荐 自定义TPOP类的描述: 源代码网推荐 源代码网推荐 SessionState = ( Init,Authorization, Transaction,Update); 源代码网推荐 TPop=class (TComponent) 源代码网推荐 public 源代码网推荐 UserName:string;//Email帐户名 源代码网推荐 PassWord:string; //Email口令 源代码网推荐 ReceText:Pchar; //server端收到的字符串 源代码网推荐 PopState:SessionState; 源代码网推荐 //pop状态: 源代码网推荐 init or authorization or transaction or update 源代码网推荐 MsgCount: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 do 源代码网推荐 begin 源代码网推荐 Host := Server; 源代码网推荐 Active := True; 源代码网推荐 end; 源代码网推荐 服务器端利用TserverSocket,一直在侦听110端口,若客户端有连接请求,则ServerSocketAccept事件会被激活,建立起连接。 源代码网推荐 源代码网推荐 procedure TMyForm.ServerSocketAccept(Sender: TObject; 源代码网推荐 Socket: TCustomWinSocket); 源代码网推荐 begin 源代码网推荐 Statusbar1.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的处理 源代码网推荐 begin 源代码网推荐 temp_command:=Socket.ReceiveText; 源代码网推荐 //to remove the crlf in command line 源代码网推荐 temp_command:=trim(copy(temp_command,1, 源代码网推荐 pos(crlf,temp_command)-1)); 源代码网推荐 pop.ReceText:=pchar(temp_command); 源代码网推荐 if pop.popstate=init then 源代码网推荐 if strLIComp(pop.ReceText,"user ",5)=0 then 源代码网推荐 pop.user 源代码网推荐 else 源代码网推荐 ServerSocket.Socket.Connections[0] 源代码网推荐 .sendtext("-ERR user name please") 源代码网推荐 else if pop.popstate=authorization then 源代码网推荐 begin 源代码网推荐 if strLIComp(pop.ReceText,"pass ",5)=0 then 源代码网推荐 pop.pass 源代码网推荐 else if strIComp(pop.ReceText,"quit")=0 then 源代码网推荐 pop.aquit 源代码网推荐 else 源代码网推荐 ServerSocket.Socket.Connections[0] 源代码网推荐 .sendtext("-ERR pass word please"); 源代码网推荐 end 源代码网推荐 else if pop.popstate=transaction then 源代码网推荐 begin 源代码网推荐 if strIComp(pop.ReceText,"stat")=0 then 源代码网推荐 pop.stat 源代码网推荐 else if strLIComp(pop.ReceText,"dele ",5)=0 then 源代码网推荐 pop.dele 源代码网推荐 else if strLIComp(pop.ReceText,"list",4)=0 then 源代码网推荐 pop.list 源代码网推荐 else if strLIComp(pop.ReceText,"retr ",5)=0 then 源代码网推荐 pop.retr 源代码网推荐 else if strIComp(pop.ReceText,"noop")=0 then 源代码网推荐 pop.noop 源代码网推荐 else if strIComp(pop.ReceText,"rset")=0 then 源代码网推荐 pop.rset 源代码网推荐 else if strIComp(pop.ReceText,"quit")=0 then 源代码网推荐 pop.tquit 源代码网推荐 else if strIComp(pop.ReceText,"last")=0 then 源代码网推荐 pop.last 源代码网推荐 else if strLIComp(pop.ReceText, "apop ",5)=0 then 源代码网推荐 pop.apop 源代码网推荐 else if strLIComp(pop.ReceText, "uidl ",5)=0 then 源代码网推荐 pop.uidl 源代码网推荐 else 源代码网推荐 ServerSocket.socket.connections[0] 源代码网推荐 .sendtext("-ERR no such command yet" crlf); 源代码网推荐 end 源代码网推荐 end; 源代码网推荐 源代码网推荐 3. 关闭连接 源代码网推荐 procedure TMyForm.ServerSocket 源代码网推荐 ClientDisconnect(Sender: TObject; 源代码网推荐 Socket: TCustomWinSocket); 源代码网推荐 begin 源代码网推荐 ServerSocket.Active := False; 源代码网推荐 //如果client端没有通过quit 命令断连, 源代码网推荐 则在断连时要把那些f_dele置为0 源代码网推荐 if pop.QuitFlag=False then 源代码网推荐 begin 源代码网推荐 MyForm.query11.Close; 源代码网推荐 MyForm.query11.Params[0].Asstring:=pop.UserName; 源代码网推荐 MyForm.query11.prepare; 源代码网推荐 MyForm.query11.execsql; 源代码网推荐 end; 源代码网推荐 end; 源代码网推荐 三. 结语 源代码网推荐 源代码网推荐 由于Email系统与数据库表结构的紧密联系,笔者没有写出各POP3命令的具体实现。相信读者在认真阅读了RFC1939之后不难写出实现函数。现在就动手为你的公司写一个自己的Email服务器吧! 源代码网推荐 源代码网供稿. |
