若何精確斷定郵件地址能否存在。本站提示廣大學習愛好者:(若何精確斷定郵件地址能否存在)文章只能為提供參考,不一定能成為您想要的結果。以下是若何精確斷定郵件地址能否存在正文
我總結了幾種郵件湧現重發、漏發的說明:1.收集;2.防火牆;3.辦事器的自我掩護,好比避免年夜批量發送時掛失落或許渣滓郵件,我認為第三種說明靠譜一些,關於碰到的這些成績鄙人面的文章中給出了解救辦法。
公司郵箱今朝應用的是Zimbra,該郵件辦事器今朝不甚穩固,常常湧現重發、漏提問題。經測試,每100封郵件僅可勝利發送98封閣下,以下是測試數據:
測試用例1:100封,總用時約:16min;實收97封,掉敗3次,3次毛病信息均為:javax.mail.MessagingException: Could not connect to SMTP host
測試用例2:100封,總用時約:16min;實收100封,掉敗2次,毛病同上。加掉敗重發機制,掉敗後期待10s重發,最多重發3次;
測試用例3:每發一封,逗留10s,總用時32min;實收100封,掉敗1次,毛病同上;重發機制同用例2.
關於MessagingException的成績,可以參考:
javax.mail.MessagingException: Could not connect to SMTP host
針對這類成績,我增長了郵件重發,
if(sendHtmlMail_(mail)){ return true; } else{ int i = 0; //包括群組郵件,掉敗不重發 boolean isNeedRe = isNeedRe(mail); while(!sendHtmlMail_(mail) && isNeedRe && i < 10){ try { i++; Thread.sleep(1000*60); } catch (InterruptedException e) { LOGGER.error("resend mail error", e); } } return true; }
但這類機制又發生了新的成績,因郵件辦事器不穩固招致在僅發送一次的情形下也會向郵件收件人發送郵件,且統一封郵件的收件人(包含抄送、密送)能夠部門收到郵件、部門收不到郵件。
針對以上的成績,我們將重發機制去除,僅針對不正當郵件(即辦事器上不存在的郵件地址)停止剔除,剔除後再停止發送。而對其他緣由招致的郵件發送掉敗不做重發(該成績將經由過程郵件辦事器運維部分向廠商反應)。
上面是斷定郵件能否正當的邏輯:
1.SMTP是任務在兩種情形下:一是電子郵件從客戶機傳輸到辦事器;二是從某一個辦事器傳輸到另外一個辦事器
2.SMTP是個要求/呼應協定,敕令和呼應都是基於ASCII文本,並以CR和LF符停止。呼應包含一個表現前往狀況的三位數字代碼
3.SMTP在TCP協定25號端口監聽銜接要求
4.銜接和發送進程
SMTP協定說龐雜也不龐雜,說簡略假如你理解Socket。不外如今只是我們應用的就是第一條中說的,從客戶機傳輸到辦事器,當我們向一台辦事器發送郵件時,郵件辦事器會起首驗證郵件發送地址能否真的存在於本辦事器上。
5 操作的步調以下:
銜接辦事器的25端口(假如沒有郵件辦事,連了也是白連)
發送helo問候
發送mail from敕令,假如前往250表現准確可以,銜接本辦事器,不然則表現辦事器須要發送人驗證。
發送rcpt to敕令,假如前往250表現則Email存在
發送quit敕令,加入銜接
基於下面這個邏輯,我們封裝郵件辦事器構成Socket,發送死令,依據前往值來斷定郵件地址能否正當:
詳細代碼以下:
import java.io.*; import java.net.*; import java.util.*; import javax.naming.*; import javax.naming.directory.*; public class SMTPMXLookup { private static int hear( BufferedReader in ) throws IOException { String line = null; int res = 0; while ( (line = in.readLine()) != null ) { String pfx = line.substring( 0, 3 ); try { res = Integer.parseInt( pfx ); } catch (Exception ex) { res = -1; } if ( line.charAt( 3 ) != '-' ) break; } return res; } private static void say( BufferedWriter wr, String text ) throws IOException { wr.write( text + "\r\n" ); wr.flush(); return; } private static ArrayList getMX( String hostName ) throws NamingException { // Perform a DNS lookup for MX records in the domain Hashtable env = new Hashtable(); env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); DirContext ictx = new InitialDirContext( env ); Attributes attrs = ictx.getAttributes ( hostName, new String[] { "MX" }); Attribute attr = attrs.get( "MX" ); // if we don't have an MX record, try the machine itself if (( attr == null ) || ( attr.size() == 0 )) { attrs = ictx.getAttributes( hostName, new String[] { "A" }); attr = attrs.get( "A" ); if( attr == null ) throw new NamingException ( "No match for name '" + hostName + "'" ); } // Huzzah! we have machines to try. Return them as an array list // NOTE: We SHOULD take the preference into account to be absolutely // correct. This is left as an exercise for anyone who cares. ArrayList res = new ArrayList(); NamingEnumeration en = attr.getAll(); while ( en.hasMore() ) { String mailhost; String x = (String) en.next(); String f[] = x.split( " " ); // THE fix ************* if (f.length == 1) mailhost = f[0]; else if ( f[1].endsWith( "." ) ) mailhost = f[1].substring( 0, (f[1].length() - 1)); else mailhost = f[1]; // THE fix ************* res.add( mailhost ); } return res; } public static boolean isAddressValid( String address ) { // Find the separator for the domain name int pos = address.indexOf( '@' ); // If the address does not contain an '@', it's not valid if ( pos == -1 ) return false; // Isolate the domain/machine name and get a list of mail exchangers String domain = address.substring( ++pos ); ArrayList mxList = null; try { mxList = getMX( domain ); } catch (NamingException ex) { return false; } // Just because we can send mail to the domain, doesn't mean that the // address is valid, but if we can't, it's a sure sign that it isn't if ( mxList.size() == 0 ) return false; // Now, do the SMTP validation, try each mail exchanger until we get // a positive acceptance. It *MAY* be possible for one MX to allow // a message [store and forwarder for example] and another [like // the actual mail server] to reject it. This is why we REALLY ought // to take the preference into account. for ( int mx = 0 ; mx < mxList.size() ; mx++ ) { boolean valid = false; try { int res; // Socket skt = new Socket( (String) mxList.get( mx ), 25 ); BufferedReader rdr = new BufferedReader ( new InputStreamReader( skt.getInputStream() ) ); BufferedWriter wtr = new BufferedWriter ( new OutputStreamWriter( skt.getOutputStream() ) ); res = hear( rdr ); if ( res != 220 ) throw new Exception( "Invalid header" ); say( wtr, "EHLO rgagnon.com" ); res = hear( rdr ); if ( res != 250 ) throw new Exception( "Not ESMTP" ); // validate the sender address say( wtr, "MAIL FROM: <[email protected]>" ); res = hear( rdr ); if ( res != 250 ) throw new Exception( "Sender rejected" ); say( wtr, "RCPT TO: <" + address + ">" ); res = hear( rdr ); // be polite say( wtr, "RSET" ); hear( rdr ); say( wtr, "QUIT" ); hear( rdr ); if ( res != 250 ) throw new Exception( "Address is not valid!" ); valid = true; rdr.close(); wtr.close(); skt.close(); } catch (Exception ex) { // Do nothing but try next host ex.printStackTrace(); } finally { if ( valid ) return true; } } return false; } public static void main( String args[] ) { String testData[] = { "[email protected]", "[email protected]", "[email protected]", // Invalid domain name "[email protected]", // Invalid address "[email protected]" // Failure of this method }; for ( int ctr = 0 ; ctr < testData.length ; ctr++ ) { System.out.println( testData[ ctr ] + " is valid? " + isAddressValid( testData[ ctr ] ) ); } return; } }
以上是斷定郵件地址能否正當的邏輯,假如郵件地址不正當,則將郵件地址從收件人列表中剔除。
private static String[] removeInvalidateAddress(String[] addresses, String mailFrom) { ArrayList<String> validateAddresses = new ArrayList<String>(); String normalAddress = null; int code; SMTPTransport smptTrans = null; if(StringUtils.isEmpty(mailFrom) || null == addresses) { return new String[0]; } String sendCmd = "MAIL FROM:" + normalizeAddress(mailFrom); try { smptTrans = (SMTPTransport)sendSession.getTransport("smtp"); smptTrans.connect(); code = smptTrans.simpleCommand(sendCmd); if(code != 250 && code != 251) { logger.error("send from invalidate" + mailFrom); } else { for(String address : addresses) { normalAddress = normalizeAddress(address); String cmd = "RCPT TO:" + normalAddress; code = smptTrans.simpleCommand(cmd); if(code == 250 || code == 251) { validateAddresses.add(address); } } } } catch(MessagingException e) { logger.error("Validate mail address error. send from " + mailFrom, e); } String[] result = validateAddresses.toArray(new String[validateAddresses.size()]); return result; } private static String normalizeAddress(String addr) { if ((!addr.startsWith("<")) && (!addr.endsWith(">"))) return "<" + addr + ">"; else return addr; }
以上是本文的全體內容,願望年夜家可以或許懂得,對年夜家有所贊助。