• <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協議規范并通過直接使用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編譯通過。
            ,
            posted on 2010-10-11 14:05 我風 閱讀(1400) 評論(0)  編輯 收藏 引用
            <2007年1月>
            31123456
            78910111213
            14151617181920
            21222324252627
            28293031123
            45678910

            常用鏈接

            留言簿(12)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊

            收藏夾

            C++

            MyFavorite

            搜索

            •  

            積分與排名

            • 積分 - 326120
            • 排名 - 75

            最新評論

            閱讀排行榜

            評論排行榜

            99久久综合国产精品免费| 国产女人aaa级久久久级| 久久伊人五月丁香狠狠色| 成人综合久久精品色婷婷| 久久99精品久久久久久hb无码| 久久久噜噜噜久久中文福利| 亚洲国产成人久久精品动漫| 久久精品国产亚洲av麻豆图片| 99精品国产在热久久无毒不卡| 久久久久亚洲精品中文字幕 | 精品久久久无码中文字幕天天| 久久久久久国产a免费观看不卡 | 久久精品国产亚洲精品| 性欧美丰满熟妇XXXX性久久久| 国产成人香蕉久久久久| 男女久久久国产一区二区三区| 久久精品亚洲福利| 久久婷婷综合中文字幕| 亚洲精品乱码久久久久久自慰| 国产午夜福利精品久久| 国内精品久久久人妻中文字幕| 亚洲欧洲精品成人久久奇米网 | 久久精品无码专区免费| 国产精品久久久亚洲| 亚洲∧v久久久无码精品| 女人高潮久久久叫人喷水| 国产精品久久久久一区二区三区| 亚洲欧美成人综合久久久| 欧美伊人久久大香线蕉综合| 欧美午夜精品久久久久久浪潮| 国产精品成人精品久久久| 久久精品九九亚洲精品天堂| 久久综合给合久久国产免费| 一本色道久久HEZYO无码| 亚洲色大成网站www久久九| 久久婷婷五月综合色奶水99啪| 亚洲伊人久久成综合人影院 | 996久久国产精品线观看| 国产精品久久午夜夜伦鲁鲁| 国内精品久久久久影院一蜜桃| 人妻精品久久久久中文字幕一冢本|