【我的Segmentfault原文】https://segmentfault.com/a/1190000008030346
前言
JavaMail的使用本身並不難,網上有不少案例,簡單易懂,而且有詳細的中文注解。但是由於JavaMail的機制設置不夠完善,特別是異常出錯時的參考信息太少,給初學者造成了不少麻煩,而我就是其中之一。在此,把我遇到過得那些坑總結出來,以免大家重蹈覆轍,浪費時間。(注:后續還有遇到新的問題,我會持續更新到這里)
一、JavaMail概述
JavaMail是由Sun定義的一套收發電子郵件的API,不同的廠商可以提供自己的實現類。但它並沒有包含在JDK中,而是作為JavaEE的一部分。
廠商所提供的JavaMail服務程序可以有選擇地實現某些郵件協議,常見的郵件協議包括:
-
SMTP:簡單郵件傳輸協議,用於發送電子郵件的傳輸協議;
-
POP3:用於接收電子郵件的標准協議;
-
IMAP:互聯網消息協議,是POP3的替代協議。
這三種協議都有對應SSL加密傳輸的協議,分別是SMTPS,POP3S和IMAPS。除JavaMail服務提供程序之外,JavaMail還需要JAF(JavaBeans Activation Framework)來處理不是純文本的郵件內容,這包括MIME(多用途互聯網郵件擴展)、URL頁面和文件附件等內容。下圖描述了JavaMail的體系結構。
(圖片來源:http://blog.csdn.net/t12x3456...)
-
mail.jar:此JAR文件包含JavaMail API和Sun提供的SMTP、IMAP和POP3服務提供程序;
-
activation.jar:此JAR文件包含JAF API和Sun的實現。
(有關JavaMail的介紹我只摘要部分,詳細介紹請參考:http://blog.csdn.net/zapldy/a...)
二、各種問題及分析說明
后面列舉出來的報錯信息需要開啟Session的debug模式,具體配置方式如下:
Session sendMailSession = Session.getInstance(pro, authenticator);
sendMailSession.setDebug(true);
1、后台顯示郵件發送成功但未收到郵件
問題現象
使用新浪郵箱發送郵件,嘗試兩種郵件發送方式,分別是“A@sina .cn發送給A@sina .cn”和“A@sina .cn發送給B@sina .cn”,摘要部分后台打印信息:
250 ok queue id 355937395546 QUIT 221 smtp-5-121.smtpsmail.fmail.xd.sinanode.com Sent message ***@sina.cn successfully....
使用163郵箱發送郵件,嘗試C@126.com發送給A@sina .cn,摘要部分后台打印信息:
250 Mail OK queued as smtp7,C8CowADnDNooqmNYHWsYGw--.30359S3 1482926655 QUIT 221 Bye Sent message ***@sina.cn successfully....
登錄新浪郵箱確認有smtp服務且處於開通狀態,也嘗試重新開啟smtp服務,仍然郵件發送不成功。網上也有不少人反饋用手機客戶端無法使用新浪郵箱發送郵件,隨后我嘗試用foxmail登錄新浪郵箱,也出現只能接收郵件而不能發送郵件的情況。
問題分析
基本確定是新浪郵箱問題,至於是smtp服務問題,還是做什么限制就不清楚了。好像平時也沒多少人用新浪郵箱發郵件,通過網頁登錄也是能發郵件的,湊合能用,畢竟是免費郵箱嘛。
這是用JavaMail發送郵件遭遇的第一個問題,案例都是參考別人原封不動拿過來用的,卻發了收不到郵件。換了幾個參考案例,問題現象相同。我開始懷疑別人給的案例代碼問題,畢竟堂堂的新浪郵箱還不至於這么不靠譜。然后,就是基於這樣的判斷,我就吃了大虧
,一直在分析代碼和配置方式,也在各大論壇搜“發送郵件成功卻收不到郵件”,發現出現問題的不在少數,而且多半給出的建議是檢查代碼有沒有問題,然后提問的人也沒了回復下文,這就導致了很大的誤導性。這正是因為這個原因,我也白白地耗了好幾天時間,直到最后發現原來原因是這么簡單...有時別人的解答能夠事半功倍,但是這種依賴性還是不靠譜的,有時自己的排錯思路更重要
。
2、向新浪郵箱發信被退信
問題現象
平時開發測試,不想用私人常用郵箱,於是注冊了搜狐郵箱,並嘗試向新浪郵箱發送郵件,不過很快搜狐郵箱收到退信(這種情況JavaMail是不會提示判斷信息的),退信內容如下:
<A@sina .cn>: host freemx1.sinamail.sina.com.cn[202.108.35.47] said:
554 Rejected due to the sending MTA's poor reputation. Please refer
http://mail.sina.com.cn/help2... Please refer to
http://chengxin.mail.sina.com... 123.125.123.1
(in reply to DATA command)
問題分析
通過訪問退信信息里面的鏈接(新浪郵箱誠信平台),基本確定搜狐郵箱服務器被拉黑了。當然,不是被新浪拉黑,而是進了RBL黑名單,新浪參考其數據進行了屏蔽。這個已經超出了個人能力范圍,果斷放棄新浪郵箱,改向其他郵箱發送。
【RBL黑名單】
RBL是英文Realtime BlackholeList的縮寫,即實時黑名單列表。在該列表中的IP地址對外發布過垃圾郵件。是由第三方的反垃圾郵件組織提供的檢查垃圾郵件發送者地址的服務。
【查詢網站】
MXToolBox:http://mxtoolbox.com/
BlackListAlert:http://www.blacklistalert.org/
3、向163郵箱發信未收到且也無未退信
問題現象
通過搜狐郵箱向新浪郵箱發信遭遇退信后,我嘗試自己發給自己,正常收到郵件。考慮模擬測試要盡量真實,我改向163郵箱發信,結果出現后台顯示發信成功,163郵箱卻沒收到郵件,但本地郵箱並沒收到退信通知。
問題分析
這說明“后台顯示郵件發送成功但未收到郵件
”的情況,原因還是多種多樣的,不僅可能發件服務器有問題,還可能是收件服務器的問題。收件服務器有的給你退信,有的還收了直接丟棄,真是什么奇葩情況都有,多加注意吧。
4、jar包重疊存在javax.mail.*
問題現象
從Oracle官網下載下來JavaMail相關jar包是mail.jar,導入進去測試后報各種奇葩錯誤。下面的異常信息是在項目中已有geronimo-javamail_1.4_spec-1.3.jar的情況下導入mail.jar后報出來的:
com.sun.mail.smtp.SMTPSendFailedException: 530 Authentication required at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:1388) at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:959) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:583)
不仔細看還以為是賬號或密碼填錯了,其實只要把geronimo-javamail_1.4_spec-1.3.jar剔除,重新發郵件就正常了。
問題分析
上面只是我貼出來的報錯情況之一,這些報錯是不一定能夠復現,因為導包就存在問題,重疊存在javax.mail.*。我是在出現第一個問題(“后台顯示郵件發送成功但未收到郵件
”)的時候,在網上看到別人說的這種情況(javaMail發送郵件成功卻收不到郵件或收到郵件無主題無收件人亂碼),而后我就開始逐個排查定位,目前通過我所知道的情況來看,重疊存在javax.mail.*的jar有mail.jar、geronimo-javamail_1.4_spec-1.x.jar、mailapi.jar和javaee.jar。
排查的方法也很簡單,比如打開javax.mail.Session,然后定位它所在的jar,剔除后再找下一個jar包。
5、jar包正確的情況下也出現報錯
問題現象
在jar包正常且配置正確的情況下,我也遇到過不少報錯情況。出現的情況基本是前幾次發郵件都正常,然后再發一次又突然出現報錯,再試一次問題又不復現,貼出幾種報錯信息如下:
(報錯1)
DEBUG SMTP: Sending failed because of invalid destination addresses
RSET
DEBUG SMTP: MessagingException while sending, THROW: javax.mail.SendFailedException: Invalid Addresses; nested exception is: com.sun.mail.smtp.SMTPAddressFailedException: 554 5.7.1 <*@163.com>: Relay access denied at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:1862) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1118)
(報錯2)
Exception in thread "main" java.lang.RuntimeException: javax.mail.MessagingException: Could not convert socket to TLS; nested exception is: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: timestamp check failed
(報錯3)
javax.mail.SendFailedException: Send failure (javax.mail.MessagingException: Could not connect to SMTP host: smtp.sohu.com, port: 25 (java.net.ConnectException: Connection timed out: connect)) at javax.mail.Transport.send(Transport.java:163) at javax.mail.Transport.send(Transport.java:48) at javamail.EmailSender.sendMail(EmailSender.java:91) at javamail.EmailSender.main(EmailSender.java:64)
(報錯4)
250-zw_71_47 250-AUTH PLAIN LOGIN 250 STARTTLS DEBUG SMTP: Found extension "AUTH", arg "PLAIN LOGIN" DEBUG SMTP: Found extension "STARTTLS", arg "" DEBUG SMTP: Attempt to authenticate using mechanisms: LOGIN PLAIN DIGEST-MD5 NTLM DEBUG SMTP: AUTH LOGIN command trace suppressed
問題分析
因為這些報錯不具有可復現性,測試過程中我也司空見慣,當然90%以上的情況郵件發送都是正常,代碼方面也是綜合了多個案例提煉出來的,而且代碼大同小異,也看過官方提供的樣例,配置內容都差不多,代碼問題可能性較小,也不排除smtp服務器抽風了,目前我暫時忽略。 當然,如果有分析出是代碼問題,也歡迎留言告知分享。(注:后面文章我會將我的代碼粘貼出來共享)
6、發信成功后,回應的信息不同
問題現象
我將JavaMail代碼在外網測試郵件發送成功時,后台打印信息結尾內容基本如下:
250 Mail OK queued as smtp7,C8CowADnDNooqmNYHWsYGw--.30359S3 1482926655 QUIT 221 Bye
當我將JavaMail代碼移植到內部環境測試郵件發送成功時,后台打印信息結尾內容如下:
250 Message accepted for delivery QUIT 221 srv201.mail.*.* SMTP Service closing transmission channel
問題分析
通過上網查詢資料得知,250和221這樣的編碼實際是smtp交互的消息編碼,其中221代表郵件會話即將結束,這意味着所有消息都已被處理。編碼后面的信息“srv201.mail.. SMTP Service closing transmission channel”和“Bye”的意思類似,可以忽略具體內容,知道221代表郵件發送正常即可。
221 | The server is closing its transmission channel. It can come with side messages like "Goodbye" or "Closing connection". | The mailing session is going to end, which simply means that all messages have been processed. |
---|
三、附錄
1、SMTP錯誤碼及建議解決方法
2、SMTP errors and reply codes
3、javaMail發送郵件成功卻收不到郵件或收到郵件無主題無收件人亂碼
4、新浪郵箱誠信平台