• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 126,  comments - 73,  trackbacks - 0
            要:本文介紹了一種采用SMTP協(xié)議規(guī)范并通過直接使用SMTP協(xié)議命令而在程序中實(shí)現(xiàn)電子郵件傳送的方法。并在VC++開發(fā)環(huán)境下給出了部分關(guān)鍵的實(shí)現(xiàn)代碼。

              前言

              電子郵件服務(wù)作為Internet上應(yīng)用最多和最廣的服務(wù)項(xiàng)目得到了非常廣泛的應(yīng)用,在網(wǎng)絡(luò)應(yīng)用中也起到非常重要的作用。如同其他的網(wǎng)絡(luò)服務(wù),電子郵件系統(tǒng)也有其使用的傳輸協(xié)議,包括SMTP(Simple Mail Transfer Protocol,簡(jiǎn)單郵件傳輸協(xié)議)、POP(Post Office Protocol,郵局協(xié)議)和IMAP(Internet Message Access Protocal,消息訪問協(xié)議)等,這些協(xié)議應(yīng)用于電子郵件的發(fā)送和接收。一些郵件處理軟件如OutLook Express和FoxMail等就是按照SMTP和POP3 協(xié)議結(jié)合Windows Sockets套接字進(jìn)行設(shè)計(jì)來收發(fā)郵件的。本文以SMTP協(xié)議為研究對(duì)象,在Visual C++ 6.0編程環(huán)境下按照SMTP協(xié)議通過套接字發(fā)送SMTP命令,接收并處理郵件服務(wù)器的反饋信息,從而實(shí)現(xiàn)對(duì)電子郵件的發(fā)送。

              SMTP協(xié)議的通訊模型和會(huì)話流程

              SMTP協(xié)議通訊模型

              SMTP協(xié)議是TCP/IP協(xié)議族中的一員,主要對(duì)如何將電子郵件從發(fā)送方地址傳送到接收方地址,也即是對(duì)傳輸?shù)囊?guī)則做了規(guī)定。SMTP協(xié)議的通信模型并不復(fù)雜,主要工作集中在發(fā)送SMTP和接收SMTP上:首先針對(duì)用戶發(fā)出的郵件請(qǐng)求,由發(fā)送SMTP建立一條連接到接收SMTP的雙工通訊鏈路,這里的接收SMTP是相對(duì)于發(fā)送SMTP而言的,實(shí)際上它既可以是最終的接收者也可以是中間傳送者。發(fā)送SMTP負(fù)責(zé)向接收SMTP發(fā)送SMTP命令,而接收SMTP則負(fù)責(zé)接收并反饋應(yīng)答??纱笾掠孟旅娴耐ㄓ嵞P褪疽鈭D來表示:


              SMTP協(xié)議的命令和應(yīng)答

              從前面的通訊模型可以看出SMTP協(xié)議在發(fā)送SMTP和接收SMTP之間的會(huì)話是靠發(fā)送SMTP的 SMTP命令和接收SMTP反饋的應(yīng)答來完成的。在通訊鏈路建立后,發(fā)送SMTP發(fā)送MAIL命令指令郵件發(fā)送者,若接收SMTP此時(shí)可以接收郵件則作出OK的應(yīng)答,然后發(fā)送SMTP繼續(xù)發(fā)出RCPT命令以確認(rèn)郵件是否收到,如果接收到就作出OK的應(yīng)答,否則就發(fā)出拒絕接收應(yīng)答,但這并不會(huì)對(duì)整個(gè)郵件操作造成影響。雙方如此反復(fù)多次,直至郵件處理完畢。SMTP協(xié)議共包含10個(gè)SMTP命令,列表如下:

            SMTP命令命令說明
            HELLO <domain> <CRLF>識(shí)別發(fā)送方到接收SMTP的一個(gè)HELLO命令
            MAIL FROM:<reverse-path><CRLF> <reverse-path>為發(fā)送者地址。此命令告訴接收方一個(gè)新郵件發(fā)送的開始,并對(duì)所有的狀態(tài)和緩沖區(qū)進(jìn)行初始化。此命令開始一個(gè)郵件傳輸處理,最終完成將郵件數(shù)據(jù)傳送到一個(gè)或多個(gè)郵箱中。
            RCPT TO:<forward-path><CRLF> <forward-path>標(biāo)識(shí)各個(gè)郵件接收者的地址
            DATA <CRLF>
            接收SMTP將把其后的行為看作郵件數(shù)據(jù)去處理,以<CRLF>.<CRLF>標(biāo)識(shí)數(shù)據(jù)的結(jié)尾。
            REST <CRLF>退出/復(fù)位當(dāng)前的郵件傳輸
            NOOP <CRLF>要求接收SMTP僅做OK應(yīng)答。(用于測(cè)試)
            QUIT <CRLF>要求接收SMTP返回一個(gè)OK應(yīng)答并關(guān)閉傳輸。
            VRFY <string> <CRLF> 驗(yàn)證指定的郵箱是否存在,由于安全因素,服務(wù)器多禁止此命令。
            EXPN <string> <CRLF> 驗(yàn)證給定的郵箱列表是否存在,擴(kuò)充郵箱列表,也常禁止使用。
            HELP <CRLF>查詢服務(wù)器支持什么命令

            注:<CRLF>為回車、換行,ASCII碼分別為13、10(十進(jìn)制)。

              SMTP協(xié)議的每一個(gè)命令都會(huì)返回一個(gè)應(yīng)答碼,應(yīng)答碼的每一個(gè)數(shù)字都是有特定含義的,如第一位數(shù)字為2時(shí)表示命令成功;為5表失??;3表沒有完成。一些較復(fù)雜的郵件程序利用該特點(diǎn),首先檢查應(yīng)答碼的首數(shù)字,并根據(jù)其值來決定下一步的動(dòng)作。下面將SMTP的應(yīng)答碼列表如下:

            應(yīng)答碼說明
            501 參數(shù)格式錯(cuò)誤
            502 命令不可實(shí)現(xiàn)
            503 錯(cuò)誤的命令序列
            504 命令參數(shù)不可實(shí)現(xiàn)
            211 系統(tǒng)狀態(tài)或系統(tǒng)幫助響應(yīng)
            214 幫助信息
            220<domain>服務(wù)就緒
            221 <domain>服務(wù)關(guān)閉
            421 <domain>服務(wù)未就緒,關(guān)閉傳輸信道
            250要求的郵件操作完成
            251 用戶非本地,將轉(zhuǎn)發(fā)向<forward-path>
            450 要求的郵件操作未完成,郵箱不可用
            550 要求的郵件操作未完成,郵箱不可用
            451 放棄要求的操作;處理過程中出錯(cuò)
            551 用戶非本地,請(qǐng)嘗試<forward-path>
            452 系統(tǒng)存儲(chǔ)不足,要求的操作未執(zhí)行
            552 過量的存儲(chǔ)分配,要求的操作未執(zhí)行
            553 郵箱名不可用,要求的操作未執(zhí)行
            354 開始郵件輸入,以"."結(jié)束
            554 操作失敗
              在應(yīng)用程序中使用SMTP協(xié)議

              SMTP協(xié)議的會(huì)話流程

              在進(jìn)行程序設(shè)計(jì)之前有必要弄清SMTP協(xié)議的會(huì)話流程,其實(shí)前面介紹的內(nèi)容已經(jīng)可以大致勾勒出用SMTP發(fā)送郵件的框架了,對(duì)于一次普通的郵件發(fā)送,其過程大致為:先建立TCP連接,隨后客戶端發(fā)出HELLO命令以標(biāo)識(shí)發(fā)件人自己的身份,并繼續(xù)由客戶端發(fā)送MAIL命令,如服務(wù)器應(yīng)答為"OK",可繼續(xù)發(fā)送RCPT命令來標(biāo)識(shí)電子郵件的收件人,在這里可以有多個(gè)RCPT行,而服務(wù)器端則表示是否愿意為收件人接受該郵件。在雙方協(xié)商結(jié)束后,用命令DATA將郵件發(fā)送出去,其中對(duì)表示結(jié)束的"."也一并發(fā)送出去。隨后結(jié)束本次發(fā)送過程,以QUIT命令退出。下面通過一個(gè)實(shí)例,從langrui@sohu.com發(fā)送郵件到renping@sina.com來更詳細(xì)直觀地描述此會(huì)話流程:

            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

              郵件的格式化

              由于電子郵件結(jié)構(gòu)上的特殊性,在傳輸時(shí)是不能當(dāng)作簡(jiǎn)單的文本來直接處理的,而必須按照一定的格式對(duì)郵件頭和郵件體進(jìn)行格式化處理之后才可以被發(fā)送。需要進(jìn)行格式化的部分主要有:發(fā)件人地址、收件人地址、主題和發(fā)送日期等。在RFC文檔的RFC 822里對(duì)郵件的格式化有詳盡的說明,有關(guān)詳情請(qǐng)參閱該文檔。下面通過VC++6.0按照RFC 822文檔規(guī)定將格式化郵件的部分編寫如下(部分代碼):

            //郵件頭準(zhǔn)備
            strTemp = _T( "From: " ) + m_strFrom; file://發(fā)件人地址
            add_header_line( (LPCTSTR)strTemp );
            strTemp = _T( "To: " ) + m_strTo; file://收件人地址
            add_header_line( (LPCTSTR)strTemp );
            m_tDateTime = m_tDateTime.GetCurrentTime();//發(fā)送時(shí)間
            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://郵件頭結(jié)束
            m_strHeader += _T( "\r\n" );
            file://郵件體準(zhǔn)備
            if( m_strBody.Right( 2 ) != _T( "\r\n" ) ) file://確認(rèn)最后以回車換行結(jié)束
            m_strBody += _T( "\r\n" );

              其中add_header_line(LPCTSTR szHeaderLine)函數(shù)用于把szHeaderLine指向的字串追加到m_strHeader后面。其中,格式化后的郵件頭保存在m_strHeader里,格式化后的郵件體保存在m_strBody中。

              由Socket套接字為SMTP提供網(wǎng)絡(luò)通訊基礎(chǔ)

              許多網(wǎng)絡(luò)程序都是采用Socket套接字實(shí)現(xiàn)的,對(duì)于一些標(biāo)準(zhǔn)的網(wǎng)絡(luò)協(xié)議如HTTP、FTP和SMTP等協(xié)議的編程也是基于套接字程序的,只是端口號(hào)不再是隨意設(shè)定而要由協(xié)議來指定,比如HTTP端口在80、FTP是21,而SMTP則是25。Socket只是提供在指定的端口上同指定的服務(wù)器從事網(wǎng)絡(luò)上的通訊能力,至于客戶和服務(wù)器之間是如何通訊的則由網(wǎng)絡(luò)協(xié)議來規(guī)定,這對(duì)于套接字是完全透明的。因此可以使用Socket套接字為程序提供網(wǎng)絡(luò)通訊的能力,而對(duì)于網(wǎng)絡(luò)通訊連路建立好之后采取什么樣的通訊應(yīng)答則要按SMTP協(xié)議的規(guī)定去執(zhí)行了。Socket套接字網(wǎng)絡(luò)編程方面的文章資料非常豐富,限于本文篇幅,在此不再贅述,有關(guān)詳情請(qǐng)參閱相關(guān)文檔。為簡(jiǎn)便起見,沒有采用編寫較復(fù)雜的Windows Sockets API進(jìn)行編程,而是使用經(jīng)過較好封裝的MFC 的CSocket類。在正式使用套接字之前,也要先用AfxSocketInit()函數(shù)對(duì)套接字進(jìn)行初始化,然后用Create()創(chuàng)建套接字對(duì)象,并由該套接字通過Connect()建立同郵件服務(wù)器的連接。如果一切正常,再后續(xù)的工作中就是遵循SMTP協(xié)議的約定來使用Send()、Receive()函數(shù)來發(fā)送SMTP命令和接收郵件服務(wù)器發(fā)來的應(yīng)答碼以完成對(duì)郵件的傳送。

              SMTP會(huì)話應(yīng)答的實(shí)現(xiàn)

              在同郵件服務(wù)器建立好鏈路連接后就可以按前面介紹過的會(huì)話流程進(jìn)行程序設(shè)計(jì)了,對(duì)于SMTP命令的發(fā)送,可按命令格式將其組幀完畢后用CSocket類的Send()函數(shù)將其發(fā)送到服務(wù)器,并通過CSocket類的Receive()函數(shù)接收從郵件服務(wù)器發(fā)來的應(yīng)答碼,并根據(jù)SMTP協(xié)議的應(yīng)答碼表對(duì)其做出響應(yīng)的處理。下面是用于接收應(yīng)答碼的函數(shù)get_response()的部分實(shí)現(xiàn)代碼:

            BOOL CSMTP::get_response( UINT response_expected )//輸入?yún)?shù)為希望的應(yīng)答碼
            {
            ……
            // m_wsSMTPServer為CSocket的類對(duì)象,調(diào)用Receive()將應(yīng)答碼接收到緩存
            // 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://檢驗(yàn)收到的應(yīng)答碼是否是所希望得到的
            if( response != pResp->nResponse )
            {
            ……//不相等的話進(jìn)行錯(cuò)誤處理
            return FALSE;
            }
            return TRUE;
            }

              會(huì)話的各個(gè)部分比較類似,都是命令--應(yīng)答方式,而且均成對(duì)出現(xiàn),下面是本文的重點(diǎn)也是實(shí)現(xiàn)的關(guān)鍵部分--在程序控制下完成對(duì)SMTP命令的格式化以及對(duì)命令的發(fā)送和對(duì)郵件服務(wù)器應(yīng)答碼的檢驗(yàn)處理:

            //格式化并發(fā)送HELLO命令,并接收、驗(yàn)證服務(wù)器應(yīng)答碼
            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://檢驗(yàn)應(yīng)答碼是否為250
            {
            ……
            return FALSE;
            }
            file://格式化并發(fā)送MAIL命令,并接收、驗(yàn)證服務(wù)器應(yīng)答碼
            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://檢驗(yàn)應(yīng)答碼是否為250
            return FALSE;
            file://格式化并發(fā)送RCPT命令,并接收、驗(yàn)證服務(wù)器應(yīng)答碼
            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://檢驗(yàn)應(yīng)答碼是否為250
            return FALSE;
            file://格式化并發(fā)送DATA命令,并接收、驗(yàn)證服務(wù)器應(yīng)答碼
            sTemp = _T( "DATA\r\n" );
            m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
            if( !get_response( DATA_SUCCESS ) ) file://檢驗(yàn)應(yīng)答碼是否為354
            return FALSE;
            file://發(fā)送根據(jù)RFC 822文檔規(guī)定格式化過的郵件頭
            m_wsSMTPServer.Send( (LPCTSTR)msg->m_strHeader, msg->m_strHeader.GetLength() );
            ……
            file://發(fā)送根據(jù)RFC 822文檔規(guī)定格式化過的郵件體
            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://發(fā)送內(nèi)容數(shù)據(jù)結(jié)束標(biāo)志"<CRLF>.<CRLF>",并檢驗(yàn)返回應(yīng)答碼
            sTemp = _T( "\r\n.\r\n" );
            m_wsSMTPServer.Send( (LPCTSTR)sTemp, sTemp.GetLength() );
            if( !get_response( GENERIC_SUCCESS ) )// 檢驗(yàn)應(yīng)答碼是否為250
            return FALSE;

              到此為止,已基本在程序中體現(xiàn)出了SMTP協(xié)議的會(huì)話流程,能在Socket套接字所提供的網(wǎng)絡(luò)通訊能力基礎(chǔ)之上實(shí)現(xiàn)以SMTP命令和SMTP應(yīng)答碼為基本會(huì)話內(nèi)容的通訊交互過程,從而最終實(shí)現(xiàn)SMTP協(xié)議對(duì)電子郵件的發(fā)送。

              結(jié)論

              電子郵件類軟件作為Internet上的應(yīng)用軟件,其設(shè)計(jì)開發(fā)必須符合Internet上成熟的技術(shù)規(guī)范(如RFC文檔系列規(guī)范)和相關(guān)協(xié)議(如POP、SMTP、IMAP以及LDAP等)。只有在遵循了上述規(guī)范和協(xié)議的基礎(chǔ)上進(jìn)行編程才能真正實(shí)現(xiàn)郵件類軟件產(chǎn)品和服務(wù)的開放性和標(biāo)準(zhǔn)化。本文著重對(duì)SMTP協(xié)議及其在VC++編程中的應(yīng)用做了介紹,并按照SMTP協(xié)議對(duì)電子郵件的發(fā)送進(jìn)行了開放性和標(biāo)準(zhǔn)性較好的程序設(shè)計(jì)。本文所述程序在Windows 98下,由Microsoft Visual C++ 6.0編譯通過。
            ,
            posted on 2010-10-11 14:05 我風(fēng) 閱讀(1400) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            <2007年3月>
            25262728123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(12)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊(cè)

            收藏夾

            C++

            MyFavorite

            搜索

            •  

            積分與排名

            • 積分 - 326155
            • 排名 - 75

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久精品一本到99热免费| 久久99精品国产麻豆婷婷| 国产成人精品久久综合| 久久久久久综合一区中文字幕 | yellow中文字幕久久网| 99久久免费国产精品热| 国产精品成人无码久久久久久 | 国产精品久久久久久五月尺| 久久毛片免费看一区二区三区| 亚洲日韩欧美一区久久久久我| 亚洲精品无码久久久久| 国产精品久久99| 久久福利青草精品资源站| 久久久久一级精品亚洲国产成人综合AV区 | 亚洲国产欧洲综合997久久| 日日狠狠久久偷偷色综合96蜜桃| 区久久AAA片69亚洲| 大香伊人久久精品一区二区| 一本色道久久综合亚洲精品| 99精品久久久久久久婷婷| 久久精品中文无码资源站| 久久电影网2021| 精品无码久久久久久久动漫 | 亚洲欧洲精品成人久久曰影片 | 久久精品国产久精国产果冻传媒| 囯产极品美女高潮无套久久久| 狠狠色婷婷久久一区二区三区| 一本一道久久a久久精品综合 | 精品国产福利久久久| 国产ww久久久久久久久久| 亚洲日韩欧美一区久久久久我 | 97精品久久天干天天天按摩| 国产免费久久久久久无码| 久久亚洲精品无码aⅴ大香 | 久久精品国产亚洲5555| 2020久久精品国产免费| 久久久久噜噜噜亚洲熟女综合| 久久久久亚洲AV成人网人人网站| 国内精品久久国产大陆| yy6080久久| 久久国产成人|