欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何準(zhǔn)確判斷郵件地址是否存在

 更新時間:2015年07月23日 11:34:17   作者:gaochaojs  
本文介紹了如何判斷郵件地址是否存在的方法,具有很高的使用價值,提高了工作效率

我總結(jié)了幾種郵件出現(xiàn)重發(fā)、漏發(fā)的解釋:1.網(wǎng)絡(luò);2.防火墻;3.服務(wù)器的自我保護(hù),比如防止大批量發(fā)送時掛掉或者垃圾郵件,我覺得第三種解釋靠譜一些,對于遇到的這些問題在下面的文章中給出了補救措施。

公司郵箱目前使用的是Zimbra,該郵件服務(wù)器目前不甚穩(wěn)定,經(jīng)常出現(xiàn)重發(fā)、漏發(fā)問題。經(jīng)測試,每100封郵件僅可成功發(fā)送98封左右,以下是測試數(shù)據(jù):
測試用例1:100封,總用時約:16min;實收97封,失敗3次,3次錯誤信息均為:javax.mail.MessagingException: Could not connect to SMTP host
測試用例2:100封,總用時約:16min;實收100封,失敗2次,錯誤同上。加失敗重發(fā)機(jī)制,失敗后等待10s重發(fā),最多重發(fā)3次;
測試用例3:每發(fā)一封,停留10s,總用時32min;實收100封,失敗1次,錯誤同上;重發(fā)機(jī)制同用例2.
關(guān)于MessagingException的問題,可以參考:
javax.mail.MessagingException: Could not connect to SMTP host
  針對這種問題,我增加了郵件重發(fā),

if(sendHtmlMail_(mail)){
    return true;
    } else{
    int i = 0;
    //包含群組郵件,失敗不重發(fā)
    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;
    }

  但這種機(jī)制又產(chǎn)生了新的問題,因郵件服務(wù)器不穩(wěn)定導(dǎo)致在僅發(fā)送一次的情況下也會向郵件收件人發(fā)送郵件,且同一封郵件的收件人(包括抄送、密送)可能部分收到郵件、部分收不到郵件。
  針對以上的問題,我們將重發(fā)機(jī)制去除,僅針對不合法郵件(即服務(wù)器上不存在的郵件地址)進(jìn)行剔除,剔除后再進(jìn)行發(fā)送。而對其他原因?qū)е碌泥]件發(fā)送失敗不做重發(fā)(該問題將通過郵件服務(wù)器運維部門向廠商反映)。
   下面是判斷郵件是否合法的邏輯:
1.SMTP是工作在兩種情況下:一是電子郵件從客戶機(jī)傳輸?shù)椒?wù)器;二是從某一個服務(wù)器傳輸?shù)搅硪粋€服務(wù)器 
2.SMTP是個請求/響應(yīng)協(xié)議,命令和響應(yīng)都是基于ASCII文本,并以CR和LF符結(jié)束。響應(yīng)包括一個表示返回狀態(tài)的三位數(shù)字代碼 
3.SMTP在TCP協(xié)議25號端口監(jiān)聽連接請求 
4.連接和發(fā)送過程 
SMTP協(xié)議說復(fù)雜也不復(fù)雜,說簡單如果你懂得Socket。不過現(xiàn)在只是我們利用的就是第一條中說的,從客戶機(jī)傳輸?shù)椒?wù)器,當(dāng)我們向一臺服務(wù)器發(fā)送郵件時,郵件服務(wù)器會首先驗證郵件發(fā)送地址是否真的存在于本服務(wù)器上。 
5 操作的步驟如下: 
連接服務(wù)器的25端口(如果沒有郵件服務(wù),連了也是白連) 
發(fā)送helo問候 
發(fā)送mail from命令,如果返回250表示正確可以,連接本服務(wù)器,否則則表示服務(wù)器需要發(fā)送人驗證。 
發(fā)送rcpt to命令,如果返回250表示則Email存在 
發(fā)送quit命令,退出連接 
基于上面這個邏輯,我們封裝郵件服務(wù)器形成Socket,發(fā)送命令,根據(jù)返回值來判斷郵件地址是否合法:
具體代碼如下:

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: <tim@orbaker.com>" );
       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[] = {
     "real@rgagnon.com",
     "you@acquisto.net",
     "fail.me@nowhere.spam", // Invalid domain name
     "arkham@bigmeanogre.net", // Invalid address
     "nosuchaddress@yahoo.com" // 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; 
  }

以上是本文的全部內(nèi)容,希望大家能夠理解,對大家有所幫助。

相關(guān)文章

  • 詳解spring boot配置 ssl

    詳解spring boot配置 ssl

    本篇文章主要介紹了詳解spring boot配置 ssl,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • spring?boot?使用Mybatis-plus查詢方法解析

    spring?boot?使用Mybatis-plus查詢方法解析

    這篇文章主要介紹了spring?boot?使用Mybatis-plus查詢方法解析,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • Java服務(wù)剛啟動時接口超時排查全過程

    Java服務(wù)剛啟動時接口超時排查全過程

    這篇文章主要為大家介紹了Java服務(wù)剛啟動時,一小波接口超時排查全過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • SpringCloud?Feign使用ApacheHttpClient代替默認(rèn)client方式

    SpringCloud?Feign使用ApacheHttpClient代替默認(rèn)client方式

    這篇文章主要介紹了SpringCloud?Feign使用ApacheHttpClient代替默認(rèn)client方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 詳解Spring如何注入靜態(tài)變量

    詳解Spring如何注入靜態(tài)變量

    這篇文章主要為大家詳細(xì)介紹了Spring是如何注入靜態(tài)變量的,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下
    2023-06-06
  • 在安卓系統(tǒng)中插入表情到光標(biāo)位置的代碼詳解

    在安卓系統(tǒng)中插入表情到光標(biāo)位置的代碼詳解

    這篇文章主要介紹了在安卓系統(tǒng)中插入表情到光標(biāo)位置的代碼詳解,利用Java代碼在EditText控件中實現(xiàn),需要的朋友可以參考下
    2015-07-07
  • 基于java實現(xiàn)的ECC加密算法示例

    基于java實現(xiàn)的ECC加密算法示例

    這篇文章主要介紹了基于java實現(xiàn)的ECC加密算法,簡單說明了ECC算法的概念、原理,并結(jié)合實例形式分析了java實現(xiàn)ECC加密算法的定義與使用技巧,需要的朋友可以參考下
    2017-08-08
  • JavaWeb之會話技術(shù)案例詳解

    JavaWeb之會話技術(shù)案例詳解

    這篇文章主要介紹了JavaWeb之會話技術(shù)案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 一文徹底弄懂零拷貝原理以及java實現(xiàn)

    一文徹底弄懂零拷貝原理以及java實現(xiàn)

    零拷貝(英語: Zero-copy) 技術(shù)是指計算機(jī)執(zhí)行操作時,CPU不需要先將數(shù)據(jù)從某處內(nèi)存復(fù)制到另一個特定區(qū)域,下面這篇文章主要給大家介紹了關(guān)于零拷貝原理以及java實現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • 深入淺析Mybatis的缺陷問題

    深入淺析Mybatis的缺陷問題

    Mybatis是業(yè)界非常流行的持久層框架,輕量級、易用,在金融IT領(lǐng)域完全是領(lǐng)軍地位,比Hibernate更受歡迎,優(yōu)勢非常多,也是非常值得我們學(xué)習(xí)的。這篇文章主要介紹了Mybatis的缺陷問題的相關(guān)資料,需要的朋友可以參考下
    2016-10-10

最新評論