高难度问题:邮件服务器之间的通信问题。(即怎样实现SMTP服务器发信的问题)(300分)

  • 主题发起人 主题发起人 运通
  • 开始时间 开始时间

运通

Unregistered / Unconfirmed
GUEST, unregistred user!
我希望的是通过与一个EMAIL地址所属的邮件(SMTP)服务器连接,然后给这个EMAIL地址发送一封
邮件过去。也就是实现SMTP服务器软件的功能,平常我们不是通过软件实现将我们的邮件发送到我
们的邮件(SMTP)服务器吗,那么服务器再发送到接者的邮件(SMTP)服务器。
那么我现在需要的是从一个SMTP邮件服务器将用户所发送的邮件发到接受者的邮件服务器上的通信
方法或解决方案。
恳请高手回答。
如果仅表示关注,请不要发帖子,而只要[订阅了此问题的邮件通知]就可以。
 
第一个[:D]
如何编写SMTP邮件服务器

来源:www.chinaasp.com

许多时候,我想不通为什么不能直接将信件送到对方(POP或IMAP)服务器上,
为什么非要通过一个莫名其妙的SMTP邮件服务器转交一次,实在不服气,如果使
用UNIX,这个问题就很好解决,用sendmail就能完成递送任务,但在Windows下
呢?同样的,一定也有办法饶开SMTP Server直接递送到对方的远程邮局服务器
上,难得住别人,难得住我们程序员吗?分析相关协议(RFC2645,RFC821,
RFC1846,RFC1939,RFC1725,RFC1730-RFC1733 etc.),我们会知道要完成直接递
送其实相当简单。
首先我们看一下Email的递送过程:
Email(Encode) -> a SMTP Relay Server -> Remote SMTP Server(远程邮局)。
非常简单,邮件编码后被递送到一个SMTP转交服务器上,该服务器对信件分检
(到同一邮局的被放在一起)后,根据优先级以及信件的先后次序被发送到远程
邮局的SMTP服务器上。换句话说,只要我们知道了SMTP转交服务器是如何确定远
程邮局SMTP服务器的地址的,就可以轻松地将饶开SMTP Relay Server直接递送到
远程邮局服务器。
SMTP Relay Server是如何确定远程邮局服务器的地址的呢?如果你熟悉域名解析,就知道是怎么回事了,我们知道电子邮件的地址由两部分构成postbox@address.com,
邮箱(postbox)和地址(address.com),给域名服务器发送指令查询"address.com”
的远程邮局服务器的地址即可找到远程邮局SMTP服务器的IP 地址,该指令查询是被
称作MX(Mail Exchange)邮件交换服务器的地址查询。远程邮局SMTP服务器的地址可
能不止一个,这时,你可根据信件优先级的不同,将对应优先级的信件发到对应地址
的远程邮局SMTP服务器,当然,你也可以不管三七二十一,随便选一个SMTP服务器发
送,见后附“域名解析结果样例”。简单吧。这下,自己编写一个SMTP Server不难了
吧!

问题:头ID是个麻烦的事,有时会产生Invalid Head ID 5811的错误。

附:域名解析结果样例

Answer List

Resource name is : sina.com
Type is : MX Class is : IN
MX Priority : 10 MX Server : mailcn.sina.com

Resource name is : sina.com
Type is : MX Class is : IN
MX Priority : 15 MX Server : mail.sina.com.cn


Authority List

Resource name is : sina.com
Type is : NS Class is : IN
Domain name is : resolver.sina.com

Resource name is : sina.com
Type is : NS Class is : IN
Domain name is : ns2.sina.com


Additional Response List

Resource name is : mailcn.sina.com
Type is : A Class is : IN
IP Address is : 202.106.184.233

Resource name is : mail.sina.com.cn
Type is : A Class is : IN
IP Address is : 202.106.187.150

Resource name is : resolver.sina.com
Type is : A Class is : IN
IP Address is : 206.204.114.135

Resource name is : ns2.sina.com
Type is : A Class is : IN
IP Address is : 209.133.24.135
 
第二个:

Email 服务器的简单实现
湖南艾邦信息技术有限公司
李岳梅
---- 贵刊1999年7月26日《编程技巧》栏目刊登的《监视POP3信箱》一文介绍了直接调用WinSock函数来进行POP3网络通信从而监视有无新邮件的方法。但我们知道从Delphi 3 开始,它自带的控件中有基于Internet开发的控件。如果我们充分利用这些控件开发Internet程序则可以简化编程工作,提高效率。鉴于目前POP3客户端的软件的种类繁多,(如Outlook Express,Foxmail 以及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 = ( 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服务器吧!
 
最后一个:补充一下,俺没有做过,只是收集了几篇文章而已。

收信服务器要能够收到别人发过来的电子邮件
按你的要求,是要写一个SMTP服务器,接受其他服务发来的邮件
以下是所有STMP命令:
HELO <SP> <domain> <CRLF>
MAIL <SP> FROM:<reverse-path> <CRLF>
RCPT <SP> TO:<forward-path> <CRLF>
DATA <CRLF>
RSET <CRLF>
SEND <SP> FROM:<reverse-path> <CRLF>
SOML <SP> FROM:<reverse-path> <CRLF>
SAML <SP> FROM:<reverse-path> <CRLF>
VRFY <SP> <string> <CRLF>
EXPN <SP> <string> <CRLF>
HELP [<SP> <string>] <CRLF>
NOOP <CRLF>
QUIT <CRLF>
TURN <CRLF>
其中 HELO
MAIL
RCPT
DATA
RSET
NOOP
QUIT
为最常用SMTP命令,使用这些命令你可以写出你想要的收信服务器
这些你都可以通过TELNET到任意一台收信服务器,如smtp.163.net上观察

以下是服务器对连接命令的返回代码
211 System status, or system help reply
214 Help message
[Information on how to use the receiver or the meaning of a
particular non-standard command; this reply is useful only
to the human user]
220 <domain> Service ready
221 <domain> Service closing transmission channel
250 Requested mail action okay, completed
251 User not local; will forward to <forward-path>

354 Start mail input; end with <CRLF>.<CRLF>

421 <domain> Service not available,
closing transmission channel
[This may be a reply to any command if the service knows it
must shut down]
450 Requested mail action not taken: mailbox unavailable
[E.g., mailbox busy]
451 Requested action aborted: local error in processing
452 Requested action not taken: insufficient system storage

500 Syntax error, command unrecognized
[This may include errors such as command line too long]
501 Syntax error in parameters or arguments
502 Command not implemented
503 Bad sequence of commands
504 Command parameter not implemented
550 Requested action not taken: mailbox unavailable
[E.g., mailbox not found, no access]
551 User not local; please try <forward-path>
552 Requested mail action aborted: exceeded storage allocation
553 Requested action not taken: mailbox name not allowed
[E.g., mailbox syntax incorrect]
554 Transaction failed

详细内容你可以参考RFC 821协议
 
你不是想做邮件服务器吧,其实要把一封信发过去简单地用对方的smtp就可以保证呵
 
对方的smtp大多数的时候是需要认证的。
 
To:yzhshi
感谢你的帮助,你所说的那些文章我也看过。
我希望知道不是我在机器上发一封邮件(因为很多软件都可以,而且我也可以自己写)。
但是我们普通发邮件是怎样发的呢?
是通过将邮件发送到我们邮件地址对应的SMTP服务器,然后服务器再通过它的方式将这个
邮件发到接受者所在的服务器,然后接受者通过POP连接服务器后就可以收到我的信了。
现在我知道了我这样将一个邮件发送到我的邮件服务器上,让他给我发出去。不过我就不
知道我的服务器是怎样将邮件发送到对方的服务器的。
我想找MX记录是一个开始,那么他可以找到对方的服务器的地址,不过我知道了它的地址
怎样和他通信才是关键。比如我知道了我的SMTP服务器地址:(比如SMTP.AA.OM)那么我可
以连接服务器,通过HELO……等指令告诉他我要给谁发一个邮件,然后邮件内容是什么等等
问题。发送OK了,然后QUIT。
不过当我只知道对方的地址的时候,我却不能给他发,为什么呢?我如果用普通发邮件的
方式,那么需要是它的用户,并拥有登陆用户名、密码。可是我没有,那么肯定的,我的服务
器是通过另外的一套通信指令来完成操作的,我就是想知道他的通信指令是什么。
 
你好像还没有仔细看我上面给你提供的文章。实际我上面给的就是smtp服务器之间的通信规则,虽然我没有仔细看。
其实你要实现的本质就是自己做一个smtp服务器,让自己去和对方的smtp服务器打交道。
 
To Yzhshi:
你的意思是,以下这些是SMTP服务器之间的通讯指令吗?
======================================================
以下是所有STMP命令:
HELO <SP> <domain> <CRLF>
MAIL <SP> FROM:<reverse-path> <CRLF>
RCPT <SP> TO:<forward-path> <CRLF>
DATA <CRLF>
RSET <CRLF>
SEND <SP> FROM:<reverse-path> <CRLF>
SOML <SP> FROM:<reverse-path> <CRLF>
SAML <SP> FROM:<reverse-path> <CRLF>
VRFY <SP> <string> <CRLF>
EXPN <SP> <string> <CRLF>
HELP [<SP> <string>] <CRLF>
NOOP <CRLF>
QUIT <CRLF>
TURN <CRLF>
其中 HELO
MAIL
RCPT
DATA
RSET
NOOP
QUIT
……………………
=====================================================
如果是,是否可以给一些注释?
 
Sorry,这些都是我收集的,我真的没有实践经验,所以就谈不上注释了。
不过上面给出的应该足够了。
 
http://netroom.hbu.edu.cn/personal/network/xieyi/smtp.htm

另,有些SMTP邮件服务需要验证,是SMTP的扩展
auth login

再把用户名和密码经base64编码后,发给SMTP服务器进行验证的
 
邮件服务器除了有SMTP的邮件发送外,还需要一个叫Relay的服务器,即邮件转发的服务,
不然,邮件只能在自己的域中传送,当Relay检查到是外部的邮件的时候,有两种方法来
完成外部邮件的发送,1,直接发送到另外一个SMTP服务器上,2,直接发送邮件到邮件所
对应的邮件服务器上的RELAY上,而不是SMTP上。
你可以试验一下SMTP.163.COM对应的是[202.108.44.205]
而它的RELAY地址有
M180.163.COM,M207.163.COM,M208.163.COMM209.163.COM,M210.163.COM
直接发送到RELAY是不需要用户验证的当然它只接收是163。com的用户,这也就
解决了SMTP服务器之间邮件发送的问题,随便说一下,邮件专递的功能就是发送到
RELAY上。

******************* http://infocaptor.3322.net/ *********************
 
多人接受答案了。
 
后退
顶部