如何准确判断邮件地址是否存在
我总结了几种邮件出现重发、漏发的解释:1.网络;2.防火墙;3.服务器的自我保护,比如防止大批量发送时挂掉或者垃圾邮件,我觉得第三种解释靠谱一些,对于遇到的这些问题在下面的文章中给出了补救措施。
公司邮箱目前使用的是Zimbra,该邮件服务器目前不甚稳定,经常出现重发、漏发问题。经测试,每100封邮件仅可成功发送98封左右,以下是测试数据:
测试用例1:100封,总用时约:16min;实收97封,失败3次,3次错误信息均为:javax.mail.MessagingException:CouldnotconnecttoSMTPhost
测试用例2:100封,总用时约:16min;实收100封,失败2次,错误同上。加失败重发机制,失败后等待10s重发,最多重发3次;
测试用例3:每发一封,停留10s,总用时32min;实收100封,失败1次,错误同上;重发机制同用例2.
关于MessagingException的问题,可以参考:
javax.mail.MessagingException:CouldnotconnecttoSMTPhost
针对这种问题,我增加了邮件重发,
if(sendHtmlMail_(mail)){ returntrue; }else{ inti=0; //包含群组邮件,失败不重发 booleanisNeedRe=isNeedRe(mail); while(!sendHtmlMail_(mail)&&isNeedRe&&i<10){ try{ i++; Thread.sleep(1000*60); }catch(InterruptedExceptione){ LOGGER.error("resendmailerror",e); } } returntrue; }
但这种机制又产生了新的问题,因邮件服务器不稳定导致在仅发送一次的情况下也会向邮件收件人发送邮件,且同一封邮件的收件人(包括抄送、密送)可能部分收到邮件、部分收不到邮件。
针对以上的问题,我们将重发机制去除,仅针对不合法邮件(即服务器上不存在的邮件地址)进行剔除,剔除后再进行发送。而对其他原因导致的邮件发送失败不做重发(该问题将通过邮件服务器运维部门向厂商反映)。
下面是判断邮件是否合法的逻辑:
1.SMTP是工作在两种情况下:一是电子邮件从客户机传输到服务器;二是从某一个服务器传输到另一个服务器
2.SMTP是个请求/响应协议,命令和响应都是基于ASCII文本,并以CR和LF符结束。响应包括一个表示返回状态的三位数字代码
3.SMTP在TCP协议25号端口监听连接请求
4.连接和发送过程
SMTP协议说复杂也不复杂,说简单如果你懂得Socket。不过现在只是我们利用的就是第一条中说的,从客户机传输到服务器,当我们向一台服务器发送邮件时,邮件服务器会首先验证邮件发送地址是否真的存在于本服务器上。
5操作的步骤如下:
连接服务器的25端口(如果没有邮件服务,连了也是白连)
发送helo问候
发送mailfrom命令,如果返回250表示正确可以,连接本服务器,否则则表示服务器需要发送人验证。
发送rcptto命令,如果返回250表示则Email存在
发送quit命令,退出连接
基于上面这个逻辑,我们封装邮件服务器形成Socket,发送命令,根据返回值来判断邮件地址是否合法:
具体代码如下:
importjava.io.*; importjava.net.*; importjava.util.*; importjavax.naming.*; importjavax.naming.directory.*; publicclassSMTPMXLookup{ privatestaticinthear(BufferedReaderin)throwsIOException{ Stringline=null; intres=0; while((line=in.readLine())!=null){ Stringpfx=line.substring(0,3); try{ res=Integer.parseInt(pfx); } catch(Exceptionex){ res=-1; } if(line.charAt(3)!='-')break; } returnres; } privatestaticvoidsay(BufferedWriterwr,Stringtext) throwsIOException{ wr.write(text+"\r\n"); wr.flush(); return; } privatestaticArrayListgetMX(StringhostName) throwsNamingException{ //PerformaDNSlookupforMXrecordsinthedomain Hashtableenv=newHashtable(); env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); DirContextictx=newInitialDirContext(env); Attributesattrs=ictx.getAttributes (hostName,newString[]{"MX"}); Attributeattr=attrs.get("MX"); //ifwedon'thaveanMXrecord,trythemachineitself if((attr==null)||(attr.size()==0)){ attrs=ictx.getAttributes(hostName,newString[]{"A"}); attr=attrs.get("A"); if(attr==null) thrownewNamingException ("Nomatchforname'"+hostName+"'"); } //Huzzah!wehavemachinestotry.Returnthemasanarraylist //NOTE:WeSHOULDtakethepreferenceintoaccounttobeabsolutely //correct.Thisisleftasanexerciseforanyonewhocares. ArrayListres=newArrayList(); NamingEnumerationen=attr.getAll(); while(en.hasMore()){ Stringmailhost; Stringx=(String)en.next(); Stringf[]=x.split(""); //THEfix************* if(f.length==1) mailhost=f[0]; elseif(f[1].endsWith(".")) mailhost=f[1].substring(0,(f[1].length()-1)); else mailhost=f[1]; //THEfix************* res.add(mailhost); } returnres; } publicstaticbooleanisAddressValid(Stringaddress){ //Findtheseparatorforthedomainname intpos=address.indexOf('@'); //Iftheaddressdoesnotcontainan'@',it'snotvalid if(pos==-1)returnfalse; //Isolatethedomain/machinenameandgetalistofmailexchangers Stringdomain=address.substring(++pos); ArrayListmxList=null; try{ mxList=getMX(domain); } catch(NamingExceptionex){ returnfalse; } //Justbecausewecansendmailtothedomain,doesn'tmeanthatthe //addressisvalid,butifwecan't,it'sasuresignthatitisn't if(mxList.size()==0)returnfalse; //Now,dotheSMTPvalidation,tryeachmailexchangeruntilweget //apositiveacceptance.It*MAY*bepossibleforoneMXtoallow //amessage[storeandforwarderforexample]andanother[like //theactualmailserver]torejectit.ThisiswhyweREALLYought //totakethepreferenceintoaccount. for(intmx=0;mx<mxList.size();mx++){ booleanvalid=false; try{ intres; // Socketskt=newSocket((String)mxList.get(mx),25); BufferedReaderrdr=newBufferedReader (newInputStreamReader(skt.getInputStream())); BufferedWriterwtr=newBufferedWriter (newOutputStreamWriter(skt.getOutputStream())); res=hear(rdr); if(res!=220)thrownewException("Invalidheader"); say(wtr,"EHLOrgagnon.com"); res=hear(rdr); if(res!=250)thrownewException("NotESMTP"); //validatethesenderaddress say(wtr,"MAILFROM:<tim@orbaker.com>"); res=hear(rdr); if(res!=250)thrownewException("Senderrejected"); say(wtr,"RCPTTO:<"+address+">"); res=hear(rdr); //bepolite say(wtr,"RSET");hear(rdr); say(wtr,"QUIT");hear(rdr); if(res!=250) thrownewException("Addressisnotvalid!"); valid=true; rdr.close(); wtr.close(); skt.close(); } catch(Exceptionex){ //Donothingbuttrynexthost ex.printStackTrace(); } finally{ if(valid)returntrue; } } returnfalse; } publicstaticvoidmain(Stringargs[]){ StringtestData[]={ "real@rgagnon.com", "you@acquisto.net", "fail.me@nowhere.spam",//Invaliddomainname "arkham@bigmeanogre.net",//Invalidaddress "nosuchaddress@yahoo.com"//Failureofthismethod }; for(intctr=0;ctr<testData.length;ctr++){ System.out.println(testData[ctr]+"isvalid?"+ isAddressValid(testData[ctr])); } return; } }
以上是判断邮件地址是否合法的逻辑,如果邮件地址不合法,则将邮件地址从收件人列表中剔除。
privatestaticString[]removeInvalidateAddress(String[]addresses,StringmailFrom) { ArrayList<String>validateAddresses=newArrayList<String>(); StringnormalAddress=null; intcode; SMTPTransportsmptTrans=null; if(StringUtils.isEmpty(mailFrom)||null==addresses) { returnnewString[0]; } StringsendCmd="MAILFROM:"+normalizeAddress(mailFrom); try { smptTrans=(SMTPTransport)sendSession.getTransport("smtp"); smptTrans.connect(); code=smptTrans.simpleCommand(sendCmd); if(code!=250&&code!=251) { logger.error("sendfrominvalidate"+mailFrom); } else { for(Stringaddress:addresses) { normalAddress=normalizeAddress(address); Stringcmd="RCPTTO:"+normalAddress; code=smptTrans.simpleCommand(cmd); if(code==250||code==251) { validateAddresses.add(address); } } } } catch(MessagingExceptione) { logger.error("Validatemailaddresserror.sendfrom"+mailFrom,e); } String[]result=validateAddresses.toArray(newString[validateAddresses.size()]); returnresult; } privatestaticStringnormalizeAddress(Stringaddr) { if((!addr.startsWith("<"))&&(!addr.endsWith(">"))) return"<"+addr+">"; else returnaddr; }
以上是本文的全部内容,希望大家能够理解,对大家有所帮助。