首页 | 邮件资讯 | 技术教程 | 解决方案 | 产品评测 | 邮件人才 | 邮件博客 | 邮件系统论坛 | 软件下载 | 邮件周刊 | 热点专题 | 工具
网络技术 | 操作系统 | 邮件系统 | 客户端 | 电子邮箱 | 反垃圾邮件 | 邮件安全 | 邮件营销 | 移动电邮 | 邮件软件下载 | 电子书下载

邮件服务器

技术前沿 | Qmail | IMail | MDaemon | Exchange | Domino | 其它 | Foxmail | James | Kerio | JavaMail | WinMail | Sendmail | Postfix | Winwebmail | Merak | CMailServer | 邮件与开发 | 金笛 |
首页 > 邮件服务器 > 邮件与程序开发 > VC++ SMTP协议电子邮件传送剖析 > 正文

VC++ SMTP协议电子邮件传送剖析

出处:信息产业部电子第二十二研究所青岛分所 作者:朗锐 时间:2005-5-31 13:11:00
摘要:本文介绍了一种采用SMTP协议规范并通过直接使用SMTP协议命令而在程序中实现电子邮件传送的方法。并在VC++开发环境下给出了部分关键的实现代码。

  前言

  电子邮件服务作为Internet上应用最多和最广的服务项目得到了非常广泛的应用,在网络应用中也起到非常重要的作用。如同其他的网络服务,电子邮件系统也有其使用的传输协议,包括SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)、POP(Post Office Protocol,邮局协议)和IMAP(Internet Message Access Protocal,消息访问协议)等,这些协议应用于电子邮件的发送和接收。一些邮件处理软件如OutLook Express和FoxMail等就是按照SMTP和POP3 协议结合Windows Sockets套接字进行设计来收发邮件的。本文以SMTP协议为研究对象,在Visual C++ 6.0编程环境下按照SMTP协议通过套接字发送SMTP命令,接收并处理邮件服务器的反馈信息,从而实现对电子邮件的发送。

  SMTP协议的通讯模型和会话流程

  SMTP协议通讯模型

  SMTP协议是TCP/IP协议族中的一员,主要对如何将电子邮件从发送方地址传送到接收方地址,也即是对传输的规则做了规定。SMTP协议的通信模型并不复杂,主要工作集中在发送SMTP和接收SMTP上:首先针对用户发出的邮件请求,由发送SMTP建立一条连接到接收SMTP的双工通讯链路,这里的接收SMTP是相对于发送SMTP而言的,实际上它既可以是最终的接收者也可以是中间传送者。发送SMTP负责向接收SMTP发送SMTP命令,而接收SMTP则负责接收并反馈应答。可大致用下面的通讯模型示意图来表示:


  SMTP协议的命令和应答

  从前面的通讯模型可以看出SMTP协议在发送SMTP和接收SMTP之间的会话是靠发送SMTP的 SMTP命令和接收SMTP反馈的应答来完成的。在通讯链路建立后,发送SMTP发送MAIL命令指令邮件发送者,若接收SMTP此时可以接收邮件则作出OK的应答,然后发送SMTP继续发出RCPT命令以确认邮件是否收到,如果接收到就作出OK的应答,否则就发出拒绝接收应答,但这并不会对整个邮件操作造成影响。双方如此反复多次,直至邮件处理完毕。SMTP协议共包含10个SMTP命令,列表如下:

SMTP命令命令说明
HELLO <domain> <CRLF>识别发送方到接收SMTP的一个HELLO命令
MAIL FROM:<reverse-path><CRLF> <reverse-path>为发送者地址。此命令告诉接收方一个新邮件发送的开始,并对所有的状态和缓冲区进行初始化。此命令开始一个邮件传输处理,最终完成将邮件数据传送到一个或多个邮箱中。
RCPT TO:<forward-path><CRLF> <forward-path>标识各个邮件接收者的地址
DATA <CRLF>
接收SMTP将把其后的行为看作邮件数据去处理,以<CRLF>.<CRLF>标识数据的结尾。
REST <CRLF>退出/复位当前的邮件传输
NOOP <CRLF>要求接收SMTP仅做OK应答。(用于测试)
QUIT <CRLF>要求接收SMTP返回一个OK应答并关闭传输。
VRFY <string> <CRLF> 验证指定的邮箱是否存在,由于安全因素,服务器多禁止此命令。
EXPN <string> <CRLF> 验证给定的邮箱列表是否存在,扩充邮箱列表,也常禁止使用。
HELP <CRLF>查询服务器支持什么命令

注:<CRLF>为回车、换行,ASCII码分别为13、10(十进制)。

  SMTP协议的每一个命令都会返回一个应答码,应答码的每一个数字都是有特定含义的,如第一位数字为2时表示命令成功;为5表失败;3表没有完成。一些较复杂的邮件程序利用该特点,首先检查应答码的首数字,并根据其值来决定下一步的动作。下面将SMTP的应答码列表如下:

应答码说明
501 参数格式错误
502 命令不可实现
503 错误的命令序列
504 命令参数不可实现
211 系统状态或系统帮助响应
214 帮助信息
220<domain>服务就绪
221 <domain>服务关闭
421 <domain>服务未就绪,关闭传输信道
250要求的邮件操作完成
251 用户非本地,将转发向<forward-path>
450 要求的邮件操作未完成,邮箱不可用
550 要求的邮件操作未完成,邮箱不可用
451 放弃要求的操作;处理过程中出错
551 用户非本地,请尝试<forward-path>
452 系统存储不足,要求的操作未执行
552 过量的存储分配,要求的操作未执行
553 邮箱名不可用,要求的操作未执行
354 开始邮件输入,以"."结束
554 操作失败
  在应用程序中使用SMTP协议

  SMTP协议的会话流程

  在进行程序设计之前有必要弄清SMTP协议的会话流程,其实前面介绍的内容已经可以大致勾勒出用SMTP发送邮件的框架了,对于一次普通的邮件发送,其过程大致为:先建立TCP连接,随后客户端发出HELLO命令以标识发件人自己的身份,并继续由客户端发送MAIL命令,如服务器应答为"OK",可继续发送RCPT命令来标识电子邮件的收件人,在这里可以有多个RCPT行,而服务器端则表示是否愿意为收件人接受该邮件。在双方协商结束后,用命令DATA将邮件发送出去,其中对表示结束的"."也一并发送出去。随后结束本次发送过程,以QUIT命令退出。下面通过一个实例,从langrui@sohu.com发送邮件到renping@sina.com来更详细直观地描述此会话流程:

R:220 sina.com Simple Mail Transfer Service Ready
S:HELLO sohu.com
R:250 sina.com
S:MAIL FROM:<langrui@sohu.com>
R:250 OK
S:RCPT TO:<renping@sina.com>
R:250 OK
S:DATA
R:354 Start mail input;end with "<CRLF>.<CRLF>"
S:……
R:250 OK
S:QUIT
R:221 sina.com Service closing transmission channel

  邮件的格式化

  由于电子邮件结构上的特殊性,在传输时是不能当作简单的文本来直接处理的,而必须按照一定的格式对邮件头和邮件体进行格式化处理之后才可以被发送。需要进行格式化的部分主要有:发件人地址、收件人地址、主题和发送日期等。在RFC文档的RFC 822里对邮件的格式化有详尽的说明,有关详情请参阅该文档。下面通过VC++6.0按照RFC 822文档规定将格式化邮件的部分编写如下(部分代码):

//邮件头准备
strTemp = _T( "From: " ) + m_strFrom; file://发件人地址
add_header_line( (LPCTSTR)strTemp );
strTemp = _T( "To: " ) + m_strTo; file://收件人地址
add_header_line( (LPCTSTR)strTemp );
m_tDateTime = m_tDateTime.GetCurrentTime();//发送时间
strTemp = _T( "Data: " );
strTemp += m_tDateTime.Format( "%a, %d %b %y %H:%M:%S %Z" );
add_header_line( (LPCTSTR)strTemp );
strTemp = _T( "Subject: " ) + m_strSubject; file://主题
add_header_line( (LPCTSTR)strTemp );
file://邮件头结束
m_strHeader += _T( "\r\n" );
file://邮件体准备
if( m_strBody.Right( 2 ) != _T( "\r\n" ) ) file://确认最后以回车换行结束
m_strBody += _T( "\r\n" );

  其中add_header_line(LPCTSTR szHeaderLine)函数用于把szHeaderLine指向的字串追加到m_strHeader后面。其中,格式化后的邮件头保存在m_strHeader里,格式化后的邮件体保存在m_strBody中。

  由Socket套接字为SMTP提供网络通讯基础

  许多网络程序都是采用Socket套接字实现的,对于一些标准的网络协议如HTTP、FTP和SMTP等协议的编程也是基于套接字程序的,只是端口号不再是随意设定而要由协议来指定,比如HTTP端口在80、FTP是21,而SMTP则是25。Socket只是提供在指定的端口上同指定的服务器从事网络上的通讯能力,至于客户和服务器之间是如何通讯的则由网络协议来规定,这对于套接字是完全透明的。因此可以使用Socket套接字为程序提供网络通讯的能力,而对于网络通讯连路建立好之后采取什么样的通讯应答则要按SMTP协议的规定去执行了。Socket套接字网络编程方面的文章资料非常丰富,限于本文篇幅,在此不再赘述,有关详情请参阅相关文档。为简便起见,没有采用编写较复杂的Windows Sockets API进行编程,而是使用经过较好封装的MFC 的CSocket类。在正式使用套接字之前,也要先用AfxSocketInit()函数对套接字进行初始化,然后用Create()创建套接字对象,并由该套接字通过Connect()建立同邮件服务器的连接。如果一切正常,再后续的工作中就是遵循SMTP协议的约定来使用Send()、Receive()函数来发送SMTP命令和接收邮件服务器发来的应答码以完成对邮件的传送。

  SMTP会话应答的实现

  在同邮件服务器建立好链路连接后就可以按前面介绍过的会话流程进行程序设计了,对于SMTP命令的发送,可按命令格式将其组帧完毕后用CSocket类的Send()函数将其发送到服务器,并通过CSocket类的Receive()函数接收从邮件服务器发来的应答码,并根据SMTP协议的应答码表对其做出响应的处理。下面是用于接收应答码的函数get_response()的部分实现代码:

BOOL CSMTP::get_response( UINT response_expected )//输入参数为希望的应答码
{
……
// m_wsSMTPServer为CSocket的类对象,调用Receive()将应答码接收到缓存
// response_buf中
m_wsSMTPServer.Receive( response_buf, RESPONSE_BUFFER_SIZE )
sResponse = response_buf;
sscanf( (LPCTSTR)sResponse.Left( 3 ), _T( "%d" ), &response );
pResp = &response_table[ response_expected ];
file://检验收到的应答码是否是所希望得到的
if( response != pResp->nResponse )
{
……//不相等的话进行错误处理
return FALSE;
}
return TRUE;
}

  会话的各个部分比较类似,都是命令--应答方式,而且均成对出现,下面是本文的重点也是实现的关键部分--在程序控制下完成对SMTP命令的格式化以及对命令的发送和对邮件服务器应答码的检验处理:

//格式化并发送HELLO命令,并接收、验证服务器应答码
gethostname( local_host, 80 );
sHello.Format( _T( "HELO %s\r\n" ), local_host );
m_wsSMTPServer.Send( (LPCTSTR)sHello, sHello.GetLength() );
if( !get_response( GENERIC_SUCCESS ) ) file://检验应答码是否为250
{
……
return FALSE;
}
file://格式化并发送MAIL命令,并接收、验证服务器应答码
sFrom.Format( _T( "MAIL From: <%s>\r\n" ), (LPCTSTR)msg->m_strFrom );
m_wsSMTPServer.Send( (LPCTSTR)sFrom, sFrom.GetLength() );
if( !get_response( GENERIC_SUCCESS ) ) file://检验应答码是否为250
return FALSE;
file://格式化并发送RCPT命令,并接收、验证服务器应答码
sEmail=(LPCTSTR)msg->m_strTo;
sTo.Format( _T( "RCPT TO: <%s>\r\n" ), (LPCTSTR)sEmail );
m_wsSMTPServer.Send( (LPCTSTR)sTo, sTo.GetLength() );
if(!get_response( GENERIC_SUCCESS )) file://检验应答码是否为250
return FALSE;
file://格式化并发送DATA命令,并接收、验证服务器应答码
sTemp = _T( "DATA\r\n" );
m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
if( !get_response( DATA_SUCCESS ) ) file://检验应答码是否为354
return FALSE;
file://发送根据RFC 822文档规定格式化过的邮件头
m_wsSMTPServer.Send( (LPCTSTR)msg->m_strHeader, msg->m_strHeader.GetLength() );
……
file://发送根据RFC 822文档规定格式化过的邮件体
sTemp = msg->m_strBody;
if( sTemp.Left( 3 ) == _T( ".\r\n" ) )
sTemp = _T( "." ) + sTemp;
while( (nPos = sTemp.Find( szBad )) > -1 )
{
sCooked = sTemp.Mid( nStart, nPos );
sCooked += szGood;
sTemp = sCooked + sTemp.Right( sTemp.GetLength() - (nPos + nBadLength) );
}
m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
file://发送内容数据结束标志"<CRLF>.<CRLF>",并检验返回应答码
sTemp = _T( "\r\n.\r\n" );
m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
if( !get_response( GENERIC_SUCCESS ) )// 检验应答码是否为250
return FALSE;

  到此为止,已基本在程序中体现出了SMTP协议的会话流程,能在Socket套接字所提供的网络通讯能力基础之上实现以SMTP命令和SMTP应答码为基本会话内容的通讯交互过程,从而最终实现SMTP协议对电子邮件的发送。

  结论

  电子邮件类软件作为Internet上的应用软件,其设计开发必须符合Internet上成熟的技术规范(如RFC文档系列规范)和相关协议(如POP、SMTP、IMAP以及LDAP等)。只有在遵循了上述规范和协议的基础上进行编程才能真正实现邮件类软件产品和服务的开放性和标准化。本文着重对SMTP协议及其在VC++编程中的应用做了介绍,并按照SMTP协议对电子邮件的发送进行了开放性和标准性较好的程序设计。本文所述程序在Windows 98下,由Microsoft Visual C++ 6.0编译通过。
,
相关文章 热门文章
  • 阻止用户发送SMTP伪造邮件
  • Python smtpd模块SMTPChannel类竞争条件拒绝服务漏洞
  • 快速清理Exchange 2003中的SMTP队列
  • Exchange 2003 SMTP服务器中继设置
  • 解析Exchange 2007中自定义SMTP标题
  • 使用加密SMTP链接Exchange问题一例
  • Microsoft Windows SMTP Server组件内存分配信息泄露漏洞(MS10-024)
  • Microsoft Windows SMTP Server组件MX记录解析拒绝服务漏洞(MS10-024)
  • qmail的smtp与pop服务及相关日志完全解决方案
  • 网易免费邮箱重新免费开放POP3/SMTP服务
  • Barracuda IM Firewall smtp_test.cgi脚本跨站脚本执行漏洞
  • 修正Qmail auth smtp中电子邮件地址任意的patch
  • 用C++ Builder实现电子邮件群发
  • 用Cdonts实现发送Email
  • Jmail的主要参数列表
  • ASP.NET 2.0发送电子邮件全面剖析之二
  • VC++ SMTP协议电子邮件传送剖析
  • 通过sina的smtp验证的Java发送邮件源代码
  • ASP.NET 2.0中发送电子邮件剖析之一
  • 在Asp.Net中使用SmtpMail发送邮件的方法
  • .NET环境下Email的技术介绍
  • ASP.NET 2.0发送电子邮件中存在的问题
  • 用ASP判断Email地址是否有效
  • IIS如何接收ServerXMLHTTP传过来的编码字符?
  • 自由广告区
     
    最新软件下载
  • SharePoint Server 2010 部署文档
  • Exchange 2010 RTM升级至SP1 教程
  • Exchange 2010 OWA下RBAC实现的组功能...
  • Lync Server 2010 Standard Edition 标..
  • Lync Server 2010 Enterprise Edition...
  • Forefront Endpoint Protection 2010 ...
  • Lync Server 2010 Edge 服务器部署文档
  • 《Exchange 2003专家指南》
  • Mastering Hyper-V Deployment
  • Windows Server 2008 R2 Hyper-V
  • Microsoft Lync Server 2010 Unleashed
  • Windows Server 2008 R2 Unleashed
  • 今日邮件技术文章
  • 腾讯,在创新中演绎互联网“进化论”
  • 华科人 张小龙 (中国第二代程序员 QQ...
  • 微软推出新功能 提高Hotmail密码安全性
  • 快压技巧分享:秒传邮件超大附件
  • 不容忽视的邮件营销数据分析过程中的算..
  • 国内手机邮箱的现状与未来发展——访尚..
  • 易观数据:2011Q2中国手机邮箱市场收入..
  • 穿越时空的爱恋 QQ邮箱音视频及贺卡邮件
  • Hotmail新功能:“我的朋友可能被黑了”
  • 入侵邻居网络发骚扰邮件 美国男子被重..
  • 网易邮箱莫子睿:《非你莫属》招聘多过..
  • 中国电信推广189邮箱绿色账单
  • 最新专题
  • 鸟哥的Linux私房菜之Mail服务器
  • Exchange Server 2010技术专题
  • Windows 7 技术专题
  • Sendmail 邮件系统配置
  • 组建Exchange 2003邮件系统
  • Windows Server 2008 专题
  • ORF 反垃圾邮件系统
  • Exchange Server 2007 专题
  • ISA Server 2006 教程专题
  • Windows Vista 技术专题
  • “黑莓”(BlackBerry)专题
  • Apache James 专题
  • 分类导航
    邮件新闻资讯:
    IT业界 | 邮件服务器 | 邮件趣闻 | 移动电邮
    电子邮箱 | 反垃圾邮件|邮件客户端|网络安全
    行业数据 | 邮件人物 | 网站公告 | 行业法规
    网络技术:
    邮件原理 | 网络协议 | 网络管理 | 传输介质
    线路接入 | 路由接口 | 邮件存储 | 华为3Com
    CISCO技术 | 网络与服务器硬件
    操作系统:
    Windows 9X | Linux&Uinx | Windows NT
    Windows Vista | FreeBSD | 其它操作系统
    邮件服务器:
    程序与开发 | Exchange | Qmail | Postfix
    Sendmail | MDaemon | Domino | Foxmail
    KerioMail | JavaMail | Winwebmail |James
    Merak&VisNetic | CMailServer | WinMail
    金笛邮件系统 | 其它 |
    反垃圾邮件:
    综述| 客户端反垃圾邮件|服务器端反垃圾邮件
    邮件客户端软件:
    Outlook | Foxmail | DreamMail| KooMail
    The bat | 雷鸟 | Eudora |Becky! |Pegasus
    IncrediMail |其它
    电子邮箱: 个人邮箱 | 企业邮箱 |Gmail
    移动电子邮件:服务器 | 客户端 | 技术前沿
    邮件网络安全:
    软件漏洞 | 安全知识 | 病毒公告 |防火墙
    攻防技术 | 病毒查杀| ISA | 数字签名
    邮件营销:
    Email营销 | 网络营销 | 营销技巧 |营销案例
    邮件人才:招聘 | 职场 | 培训 | 指南 | 职场
    解决方案:
    邮件系统|反垃圾邮件 |安全 |移动电邮 |招标
    产品评测:
    邮件系统 |反垃圾邮件 |邮箱 |安全 |客户端
    广告联系 | 合作联系 | 关于我们 | 联系我们 | 繁體中文
    版权所有:邮件技术资讯网©2003-2010 www.5dmail.net, All Rights Reserved
    www.5Dmail.net Web Team   粤ICP备05009143号