判断邮件地址是否存在的方法

  公司邮箱目前使用的是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

  我看了一下几种解释:1.网络;2.防火墙;3.服务器的自我保护,比如防止大批量发送时挂掉或者垃圾邮件,我觉得第三种解释靠谱一些。

针对这种问题,我增加了邮件重发,

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;  
	}

相关文章供参考:

Java Scoket实现发送Mail

java处理邮箱地址是否真实存在的实例

Java与邮件系统交互之使用Socket验证邮箱是否存在

时间: 2024-10-25 00:53:19

判断邮件地址是否存在的方法的相关文章

判断邮件地址是否是真实地址

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 ( (l

C-循环,获取数组地址的几种方法

程序的调试的作用: 跟踪CPU执行代码的步骤 监视变量的值在程序执行的时候是如何变化的 do-while 和 while 在实际的开发中, do-while比较少用 因为就算循环无论如何要至少执行一次的时候,while也可以搞定 循环的情况一共就两种: 1.循环次数确定的循环 2.循环次数不确定的循环,但是确定了循环继续活着结束的条件 对于一个一位数组来说 1.获取 a[i]的地址的几种方法 1 &a[i] // 取地址符 1 a+i // 数组名就是数组首地址 1 int *p = a; 2

java正则表达式小练习(IP地址检测、排序,叠词的处理,邮件地址的获取)

import java.util.Arrays; import java.util.Comparator; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; class MyComparator implements Comparator<String>{ public int compare(String ip1, String ip2) { Pattern p

C# 判断ip地址是否正确

最后要用一方法判断ip地址是否正确,直接用.Net现成的类,方法如下: string ipStr="192.168.222.333"; IPAddress ip; if(IPAddress.TryParse(ipStr,out ip)) { Console.WriterLine("合法IP"); } else { Console.WriterLine("非法IP"); }

获取用户的邮箱地址的几个方法

用户的邮箱地址获取是一个长期的过程,许可式邮件营销,是得到用户许可之后,通过电子邮件向客户发送产品和服务信息的.如何获取用户的邮箱地址呢?本文给大家介绍一下. 一.精简的订阅信息. 很多人喜欢长篇大论的介绍订阅信息,其实这是一个误区.简单的介绍下订阅信息即可.后续的内容可以等到用户订阅后再介绍. 二.将订阅按钮放在最显眼的位置. 通过放在最显眼的位置,可以让用户第一时间看到,并且订阅. 三.二维码的运用. 现在只能手机终端客户越来越多,如果你线下业务很多,那这个方法可一定不要错过哦. 四.分享有

米老师如果处理邮件地址错误事件

背景 前几天我们九期所有成员进行了一次特殊的考试,很高兴,自己当天在机房(一般都在),所以,也参加了这次的考试. 试卷的内容非常的好,题非常的经典,所以,考后想再看看相应题,于是,考后找昌哥要卷子,昌哥说没有了,然后,就和米老师交流要卷子,在要卷子的过程发生了一件让人深思的事情. 事件内容 考完试的第二天12点左右,通过飞信联系米老师,希望能够马上拿到一份卷子,米老师收到信息后,回信说,我马上给你一份电子版的,然后,几秒钟后,米老师给我回信说,已经发过去了,于是,我马上收邮件,结果没有收到,当时

关于企业邮箱服务器经常被spamhaus反垃圾邮件组织拦截的排错方法分享

其实在企业邮件平台管理过程中,企业邮箱管理员经常会被各种各样的邮箱问题所困扰,而困扰最大的除了服务器规划不当带来的后期服务器维护的困难外,还有最致命也是最让邮件管理员着急的事情,那就是企业邮箱经常被一些国际反垃圾邮件组织列为黑名单,也许,很多人会在想,如果是列为了黑名单,那么直接进行申诉,释放就可以了,这个回答其实也勉强可以算是对的,但是从申诉到释放,其实也是需要等待一个时间的,那么这时出现大批量的用户反映,我个人认为这其实也是邮件平台不稳定的一个体现,做为管理员,则必须要了解被列为黑名单的根本

C#判断某程序是否运行的方法

本文实例讲述了C#判断某程序是否运行的方法,分享给大家供大家参考. 具体实现方法如下: [DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); [DllImp

判断js中的数据类型的方法

在 判断js中的数据类型 我们通常会使用typeOf()方法,        typeof   2         输出   number      typeof   null       输出   object        typeof   {}      输出   object        typeof    []     输出   object        typeof   (function(){}) 输出  function        typeof    undefined