JavaMail 發送郵件看似簡單,實則暗藏 DNS 解析、端口協議、SSL/TLS 協商等網絡層陷阱。本文記錄一次從「Unknown SMTP host」到「Could not connect to SMTP host」的排查全過程,揭示 MailSSLSocketFactory 強制啟用 SSL 導致端口不匹配的核心坑點。
mail.chinamobilesz.comjavax.mail:mail:1.4.7javax.mail.MessagingException: Unknown SMTP host: mail.chinamobilesz.com
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1959)
分析:DNS 解析失敗。Linux 服務器 /etc/hosts 無該域名映射,且 DNS 服務器未生效。
在 /etc/hosts 添加 IP mail.chinamobilesz.com 映射後,telnet mail.chinamobilesz.com 25 連通,但 Java 程式仍報錯:
javax.mail.MessagingException: Could not connect to SMTP host: mail.chinamobilesz.com, port: 25
明明 25 端口通,為什麼 Java 連不上?
項目郵件工具類中發現如下片段:
// SSL加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.ssl.socketFactory", sf);
mail.smtp.ssl.enable=true:JavaMail 會**忽略**默認 25 端口,改用 **465 端口**(SMTPS 隱式 SSL)。MailSSLSocketFactory:自定義 SSL Socket 工廠,繞過證書驗證(setTrustAllHosts)。結論:代碼強制啟用隱式 SSL,導致客戶端嘗試連 465 端口,服務端無此端口,連接超時拋出「Could not connect... port: 25」(錯誤信息誤導,實際嘗試 465)。
刪除上述 MailSSLSocketFactory 相關代碼,恢復默認 25 端口明文/STARTTLS 協商。
props.put("mail.smtp.starttls.enable", "true"); // 25/587 端口升級 TLS
props.put("mail.smtp.starttls.required", "true"); // 強制要求 TLS
此模式下客戶端連 25 端口,通過 SMTP STARTTLS 指令協商升級加密通道,兼容性最佳。
// 僅在 STARTTLS 模式下需自定義 SocketFactory
props.put("mail.smtp.ssl.trust", "mail.chinamobilesz.com"); // JDK 1.8+ 支持
// 或繼續用 MailSSLSocketFactory 但配合 starttls.enable
| 協議 | 端口 | 加密模式 | JavaMail 關鍵屬性 |
|---|---|---|---|
| SMTP 明文 | 25 | 無 | 默認 |
| SMTPS (隱式 SSL) | 465 | 連接即 SSL | mail.smtp.ssl.enable=true |
| SMTP + STARTTLS | 25/587 | 明文→協商 TLS | mail.smtp.starttls.enable=true |
telnet host 25 是否通?props.put("mail.smtp.auth", "true")。JavaMail 發送郵件的核心難點不在 API,而在於**網絡協議與服務商能力的匹配**。理解 SMTP/SMTPS/STARTTLS 三種模式的端口與握手差異,配合 telnet/openssl s_client 驗證連通性,才能快速定位「連不上」的真凶。