如何准确判断邮件地址是否存在

我总结了几种邮件出现重发、漏发的解释: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: <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;
  }

以上是本文的全部内容,希望大家能够理解,对大家有所帮助。

(0)

相关推荐

  • java实现基于SMTP发送邮件的方法

    本文实例讲述了java实现基于SMTP发送邮件的方法.分享给大家供大家参考.具体实现方法如下: import java.util.Date; import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Tra

  • C# 邮件地址是否合法的验证

    复制代码 代码如下: /// <summary> /// 检测输入的邮件地址strEmail是否合法,非法则返回true. /// </summary> public bool CheckEmail(string strEmail) { int i, j; string strTmp, strResult; string strWords = "abcdefghijklmnopqrstuvwxyz_-.0123456789"; //定义合法字符范围 bool b

  • 如何准确判断邮件地址是否存在

    我总结了几种邮件出现重发.漏发的解释:1.网络:2.防火墙:3.服务器的自我保护,比如防止大批量发送时挂掉或者垃圾邮件,我觉得第三种解释靠谱一些,对于遇到的这些问题在下面的文章中给出了补救措施. 公司邮箱目前使用的是Zimbra,该邮件服务器目前不甚稳定,经常出现重发.漏发问题.经测试,每100封邮件仅可成功发送98封左右,以下是测试数据: 测试用例1:100封,总用时约:16min:实收97封,失败3次,3次错误信息均为:javax.mail.MessagingException: Could

  • VB.NET验证邮件地址的合法性实现代码

    一.提出问题 现在,对于用户在Web页面上或电话中给出的Email地址,我们越来越不敢肯定它是否真的有效.在今天这个垃圾邮件泛滥成灾的年代,人们完全有理由舍不得轻易透露Email地址. 另一方面,对于通过正规途径得到的邮件地址,当我们将它用于合法的目的时,也常常要为邮件地址是否有效而烦恼,用户可能有意或无意地写错地址,也可能由于长时间不访问而导致邮箱失效.对于少量的邮件地址,也许可以手工验证其合法性,例如发送测试email:但是,当邮件地址的数量达到数万甚至更多时,手工验证就不可能了,必须用专门

  • js正则表达式验证邮件地址

    我们最经常遇到的验证,就是电子邮件地址验证.网站上常见.各种网页脚本也都常用"正则表达式"(regular expression)对我们输入的电子邮件地址进行验证,判断是否合法.有的还能分解出用户名和域名.现在用JavaScript语言实现一下电子邮件地址验证程序,用的是JavaScript语言的正则表达式库. 效果图: 不合法的情况: 合法的情况: 源代码如下,该网页已在IE.Firefox.Chrome上验证通过: <!DOCTYPE html PUBLIC "-/

  • Java判断IP地址为内网IP还是公网IP的方法

    本文实例讲述了Java判断IP地址为内网IP还是公网IP的方法.分享给大家供大家参考.具体分析如下: tcp/ip协议中,专门保留了三个IP地址区域作为私有地址,其地址范围如下: 10.0.0.0/8:10.0.0.0-10.255.255.255 172.16.0.0/12:172.16.0.0-172.31.255.255 192.168.0.0/16:192.168.0.0-192.168.255.255 那么,直接上代码吧: 复制代码 代码如下: public static boolea

  • java在网页上面抓取邮件地址的方法

    本文实例讲述了java在网页上面抓取邮件地址的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: import java.io.BufferedReader;  import java.io.InputStreamReader;  import java.net.URL;  import java.util.regex.Matcher;  import java.util.regex.Pattern;    public class h1  {     public stati

  • Python获取邮件地址的方法

    本文实例讲述了Python获取邮件地址的方法.分享给大家供大家参考.具体实现方法如下: import email.Utils def getCleanMailAddress(strAddr): emails = email.Utils.parseaddr(strAddr.lower()) return emails[1] 希望本文所述对大家的Python程序设计有所帮助.

  • php正则表达式验证(邮件地址、Url地址、电话号码、邮政编码)

    本文实例需要验证的内容:邮件地址.Url地址.电话号码.邮政编码,验证方法分享给大家供大家参考,具体内容如下 1.电子邮件地址的校验 <?php /* 校验邮件地址*/ function checkMail($email) { //用户名,由"\w"格式字符."-"或"."组成 $email_name= "\w|(\w[-.\w]*\w)"; //域名中的第一段,规则和用户名类似,不包括点号"." $

  • 用asp实现网址和邮件地址的转换函数

    网址和邮件地址的转换函数<% function URLconvert(str)  convertstr=str urlLocation=instr(convertstr,"http://") if (urlLocation=0) and (mailLocation=0) then  URLconvert=convertstr exit function end if if urlLocation<>0 then rightstr=str Do while urlLoc

  • Android 判断ip地址合法实现代码

    Android 判断ip地址合法实现代码 昨天,我在网上找了一下判断文本框内的字符串是否是合法的ip地址,自己想到了正则表达式,可是自己对这个却不熟,所以在网上查找了一些资料,最后终于是做出来了. 具体的代码很简单, public boolean isIP(String addr) { if(addr.length() < 7 || addr.length() > 15 || "".equals(addr)) { return false; } /** * 判断IP格式和范

随机推荐