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

简介:

  公司邮箱目前使用的是Zimbra,该邮件服务器目前不甚稳定,经常出现重发、漏发问题。经测试,每100封邮件仅可成功发送98封左右,以下是测试数据:

测试用例1100封,总用时约:16min;实收97封,失败3次,3次错误信息均为:javax.mail.MessagingException: Could not connect to SMTP host

测试用例2100封,总用时约:16min;实收100封,失败2次,错误同上。加失败重发机制,失败后等待10s重发,最多重发3次;

测试用例3:每发一封,停留10s,总用时32min;实收100封,失败1次,错误同上;重发机制同用例2.

关于MessagingException的问题,可以参考:

javax.mail.MessagingException: Could not connect to SMTP host

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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.SMTPTCP协议25号端口监听连接请求  

4.连接和发送过程  

SMTP协议说复杂也不复杂,说简单如果你懂得Socket。不过现在只是我们利用的就是第一条中说的,从客户机传输到服务器,当我们向一台服务器发送邮件时,邮件服务器会首先验证邮件发送地址是否真的存在于本服务器上。  

5 操作的步骤如下:  

连接服务器的25端口(如果没有邮件服务,连了也是白连)  

发送helo问候  

发送mail from命令,如果返回250表示正确可以,连接本服务器,否则则表示服务器需要发送人验证。  

发送rcpt to命令,如果返回250表示则Email存在  

发送quit命令,退出连接  

基于上面这个逻辑,我们封装邮件服务器形成Socket,发送命令,根据返回值来判断邮件地址是否合法:

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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 ;
      }
}

  以上是判断邮件地址是否合法的逻辑,如果邮件地址不合法,则将邮件地址从收件人列表中剔除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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验证邮箱是否存在



     本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1674178,如需转载请自行联系原作者


相关文章
|
4月前
|
Java 数据安全/隐私保护
JavaBean组件<jsp:forward>动作<jsp:param>动作登录页面输入用户名和密码,然后进入检查页面判断是否符合要求,符合要求跳转到成功界面,不符合要求返回登录界面,显示错误信息。
该博客文章通过JavaBean组件和JSP动作元素`<jsp:forward>`与`<jsp:param>`的使用示例,演示了用户登录流程,包括登录信息的提交、验证以及根据验证结果进行的页面跳转。
JavaBean组件<jsp:forward>动作<jsp:param>动作登录页面输入用户名和密码,然后进入检查页面判断是否符合要求,符合要求跳转到成功界面,不符合要求返回登录界面,显示错误信息。
|
4月前
|
JavaScript 前端开发
最万能的登录、注册页。带输入数据的验证。邮箱的正则表达式、正则表达式验证输入的数据
这篇文章提供了一个带有输入数据验证功能的万能登录和注册页面的HTML和JavaScript代码示例,包括正则表达式验证邮箱和用户输入数据。
|
7月前
|
弹性计算 运维 Shell
判断管理员(字符串)
【4月更文挑战第29天】
45 2
|
7月前
|
机器学习/深度学习 弹性计算 运维
判断用户输入的是 Yes 或 NO
【4月更文挑战第29天】
47 1
|
7月前
|
存储 弹性计算 Shell
|
7月前
|
SQL
查找重复的电子邮箱
查找重复的电子邮箱
64 0
|
7月前
|
SQL C++
删除重复的电子邮箱(C++)
删除重复的电子邮箱(C++)
38 0
提交表单时input字段非空验证
提交表单时input字段非空验证
51 0
|
JavaScript
正则表达式判断是不是手机号
正则表达式判断是不是手机号
95 0
|
网络安全 数据安全/隐私保护