501 Command "HELO" requires an argument問(wèn)題的解決方法
場(chǎng)景描述:
保存郵箱配置時(shí)自動(dòng)探測(cè)郵箱配置參數(shù)是否正確,結(jié)果發(fā)現(xiàn)在探測(cè)SMTP時(shí),系統(tǒng)報(bào)出如下異常:
javax.mail.MessagingException: 501 Command "HELO" requires an argument
at com.sun.mail.smtp.SMTPTransport.issueCommand(SMTPTransport.java:1363)
at com.sun.mail.smtp.SMTPTransport.helo(SMTPTransport.java:838)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:375)
at javax.mail.Service.connect(Service.java:275)
但是換一個(gè)windows服務(wù)器,發(fā)現(xiàn)就沒(méi)這樣的問(wèn)題,僅在一臺(tái)Linux服務(wù)器上可以重現(xiàn),直觀感覺(jué)就是這臺(tái)Linux服務(wù)器某些配置有問(wèn)題。
排查步驟
1. 找一臺(tái)同樣操作系統(tǒng)的Linux服務(wù)器,驗(yàn)證郵箱配置,ok,排除Linux操作系統(tǒng)特殊性的問(wèn)題
2. 直接在Linux服務(wù)器上使用telnet連接對(duì)端郵件服務(wù)器的SMTP端口,OK,排除該服務(wù)器的網(wǎng)絡(luò)問(wèn)題
3. 查找HELO指令解釋
HELO
-- Initiates a conversation with the mail server. When using this command you can specify your domain name so that the mail server knows who you are. For example, HELO mailhost2. cf.ac.uk.
發(fā)現(xiàn)HELO指令后面需要跟一個(gè)發(fā)起者的主機(jī)名,告訴SMTP服務(wù)器這個(gè)消息來(lái)源是哪里。
再看異常信息是"501 Command "HELO" requires an argument",很明顯,程序在跟SMTP SERVER交互過(guò)程中沒(méi)有傳遞源主機(jī)域名。
4. 查看JAVA MAIL源碼
查找HELO指令,如下:
/**
* Issue the <code>HELO</code> command.
*
* @param domain
* our domain
*
* @since JavaMail 1.4.1
*/
protected void helo(String domain) throws MessagingException {
if (domain != null)
issueCommand("HELO " + domain, 250);
else
issueCommand("HELO", 250);
}
查找helo方法在哪里被調(diào)用,看看domain如何被傳遞過(guò)來(lái)的
if (useEhlo)
succeed = ehlo(getLocalHost());
if (!succeed)
helo(getLocalHost());
順理成章,接著找getLocalHost()方法,定義如下:
/**
* Get the name of the local host, for use in the EHLO and HELO commands.
* The property mail.smtp.localhost overrides mail.smtp.localaddress, which
* overrides what InetAddress would tell us.
*/
public synchronized String getLocalHost() {
try {
// get our hostname and cache it for future use
if (localHostName == null || localHostName.length() <= 0)
localHostName = session.getProperty("mail." + name + ".localhost");
if (localHostName == null || localHostName.length() <= 0)
localHostName = session.getProperty("mail." + name+ ".localaddress");
if (localHostName == null || localHostName.length() <= 0) {
InetAddress localHost = InetAddress.getLocalHost();
localHostName = localHost.getHostName();
// if we can't get our name, use local address literal
if (localHostName == null)
// XXX - not correct for IPv6
localHostName = "[" + localHost.getHostAddress() + "]";
}
} catch (UnknownHostException uhex) {
}
return localHostName;
}
可以看到hostname的獲取可以通過(guò)當(dāng)前連接的session屬性中獲取,也可以從當(dāng)前服務(wù)器的hosts配置中獲取,而我們程序是沒(méi)有在session中設(shè)置hostname的,因此原因肯定在于該臺(tái)Linux服務(wù)器的hosts文件被修改,造成JAVA程序無(wú)法自動(dòng)獲得localhostName。
5. 查看/etc/hosts文件,情況如下:
127.0.0.1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6
簡(jiǎn)單的將hosts文件修改如下:
127.0.0.1 localhost
::1 localhost6.localdomain6 localhost6
6. 重新測(cè)試,問(wèn)題解決了。
其實(shí),這種情況也可以通過(guò)程序避免,即在session連接中加入當(dāng)前服務(wù)器的hostname屬性,程序示例:
public static void main(String[] args) {
try {
int smtpport = 25;
String smtpserver = "219.147.xxx.xxx";
Properties prop = System.getProperties();
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.localhost", "myMailServer");
Session mailSession = Session.getInstance(prop, null);
Transport transport = mailSession.getTransport("smtp");
transport.connect(smtpserver,smtpport, "username", "password");
System.out.println("connect ok");
transport.close();
} catch (AuthenticationFailedException en) {
en.printStackTrace();
System.out.println("smtp服務(wù)器連接失敗,請(qǐng)檢查輸入信息是否正確");
} catch (NoSuchProviderException e) {
e.printStackTrace();
System.out.println("smtp服務(wù)器連接失敗,請(qǐng)檢查輸入信息是否正確");
} catch (MessagingException e) {
e.printStackTrace();
System.out.println("smtp服務(wù)器連接失敗,請(qǐng)檢查輸入信息是否正確");
}
}
相關(guān)文章
SpringBoot如何通過(guò)配置文件(yml,properties)限制文件上傳大小
這篇文章主要介紹了SpringBoot如何通過(guò)配置文件(yml,properties)限制文件上傳大小,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java中List的contains()方法的使用小結(jié)
List?的?contains()?方法用于檢查列表中是否包含指定的元素,借助equals()方法進(jìn)行判斷,下面就來(lái)介紹Java中List的contains()方法的使用小結(jié),感興趣的可以了解一下2025-04-04
Springcloud hystrix服務(wù)熔斷和dashboard如何實(shí)現(xiàn)
這篇文章主要介紹了Springcloud hystrix服務(wù)熔斷和dashboard如何實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12
項(xiàng)目打包成jar后包無(wú)法讀取src/main/resources下文件的解決
本文主要介紹了項(xiàng)目打包成jar后包無(wú)法讀取src/main/resources下文件的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
Java并發(fā)編程中的ReentrantLock類詳解
這篇文章主要介紹了Java并發(fā)編程中的ReentrantLock類詳解,ReentrantLock是juc.locks包中的一個(gè)獨(dú)占式可重入鎖,相比synchronized,它可以創(chuàng)建多個(gè)條件等待隊(duì)列,還支持公平/非公平鎖、可中斷、超時(shí)、輪詢等特性,需要的朋友可以參考下2023-12-12
Java中string和int的互相轉(zhuǎn)換問(wèn)題
本文通過(guò)實(shí)例代碼給大家詳細(xì)介紹了Java中string和int的互相轉(zhuǎn)換問(wèn)題,感興趣的朋友一起看看吧2017-10-10
SpringBoot設(shè)置接口超時(shí)的方法小結(jié)
這篇文章主要介紹了SpringBoot設(shè)置接口超時(shí)的方法小結(jié),包括配置文件,config配置類及相關(guān)示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
SpringBoot使用Feign進(jìn)行服務(wù)間通信的實(shí)現(xiàn)示例代碼
Feign是一個(gè)開源的Java HTTP客戶端,可以幫助我們?cè)赟pringBoot應(yīng)用中快速構(gòu)建和使用HTTP客戶端,方便實(shí)現(xiàn)服務(wù)間的通信,本文就來(lái)介紹一下SpringBoot使用Feign進(jìn)行服務(wù)間通信的實(shí)現(xiàn)示例代碼,感興趣的可以了解一下2024-01-01

