用户操作
[即时聊天] [发私信] [加为好友]
虎0406ID:hu0406
17183次访问,排名6941好友2人,关注者2
苦心人,天不负,破釜沉舟,百二秦川终归蜀;有志者,事竟成,卧薪尝胆,三千越甲可吞吴。
hu0406的文章
原创 17 篇
翻译 0 篇
转载 40 篇
评论 13 篇
虎0406的公告
苦心人,天不负,破釜沉舟,百二秦川终归蜀;有志者,事竟成,卧薪尝胆,三千越甲可吞吴。
最近评论
呀呀:谢谢你的提供和劳动
边诗涵:谢谢
流星雨:太酷了,好有用啊!!!!!
爱情:太帅了 ,谢了
枫叶520:谢谢
文章分类
收藏
    相册
    My photo
    编程
    充电港湾
    codeProject
    Kevins
    MSDN
    VCer
    判官的Blog
    协议及相关
    开发者的乐园
    看雪学院
    老罗的缤纷天地
    邮件技术咨询网
    邹青峰野狼一个愚笨的程序员的成长之路
    技术Blog
    c0ffee1982的专栏
    stealthwalker
    西邮陈老师
    休闲小酌
    存档
    订阅我的博客
    XML聚合  FeedSky

    转载 SMTP协议收藏

    新一篇: 博大考神》2006版之Word2003题库版操作提示 | 旧一篇: HTTP协议基础

    电子邮件服务作为Internet上应用最多和最广的服务项目得到了非常广泛的应用,在网络应用中也起到非常重 要的作用。如同其他的网络服务,电子邮件系统也有其使用的传输协议,包括SMTPSimple Mail Transfer Protocol,简单邮件传输协议)、POPPost Office Protocol,邮局协议)和IMAPInternet Message Access Protocal,消息访问协议)等,这些协议应用于电子邮件的发送和接收。一些邮件处理软件如OutLook ExpressFoxMail等就是按照SMTPPOP3 协议结合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协议共包含10SMTP命令,列表如下:

    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码分别为1310(十进制)。

      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套接字实现的,对于一些标准的网络协议如HTTPFTPSMTP等协议的编程也是基于套接字程序的,只是端口号 不再是随意设定而要由协议来指定,比如HTTP端口在80FTP21,而SMTP则是25Socket只是提供在指定的端口上同指定的服务器从事网 络上的通讯能力,至于客户和服务器之间是如何通讯的则由网络协议来规定,这对于套接字是完全透明的。因此可以使用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文档系列规范)和相关协议(如 POPSMTPIMAP以及LDAP等)。只有在遵循了上述规范和协议的基础上进行编程才能真正实现邮件类软件产品和服务的开放性和标准化。本文着重 对SMTP协议及其在VC++编程中的应用做了介绍,并按照SMTP协议对电子邮件的发送进行了开放性和标准性较好的程序设计。本文所述程序在 Windows 98下,由Microsoft Visual C++ 6.0编译通过。

     

     

    发表于 @ 2008年03月07日 11:39:00|评论(loading...)|编辑

    新一篇: 博大考神》2006版之Word2003题库版操作提示 | 旧一篇: HTTP协议基础

    评论:没有评论。

    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © 虎0406