最近遇到一个需求:需要验证用户填写的邮箱地址是否真实存在,是否可达。和普通的正则表达式不同,他要求尝试链接目标邮箱服务器并请求校验目标邮箱是否存在。

先来了解


  DNS之MX记录

  对于DNS不了解的,请移步百度搜索。

  DNS中除了A记录(域名-IP映射)之外,还有MX记录(邮件交换记录),CNAME记录(别名,咱不管)。

  MX记录就是为了在发送邮件时使用友好域名规则,比如我们发送到QQ邮箱xxx@qq.com。我们填写地址是到“qq.com”,但实际上可能服务器地址千奇百怪/而且许有多个。在设置DNS时可以顺带设置MX记录。

  说白了,“qq.com”只是域名,做HTTP请求响应地址,你邮件能发到Tomcat上吗?那我们发到“qq.com”上面的邮件哪里去了,我们把自己想象成一个邮件服务器,你的用户让你给xxx@qq.com发一封信,你如何操作?找mx记录是必要的。

  SMTP之纯Socket访问

  对于SMTP不了解或Java Socket不了解的,请移步百度搜索。

  邮件协议是匿名协议,我们通过SMTP协议可以让邮件服务器来验证目标地址是否真实存在。

代码实现


  由以上介绍可知:通过DNS中MX记录可以找到邮件服务器地址,通过SMTP协议可以让邮件服务器验证目标邮箱地址的真实性。

  那么我们就来进行编码实现。

  首先需要查询DNS,这个需要用到一个Java查询DNS的组件dnsjava(下载),自己写太麻烦。

 1 // 查找mx记录
 2             Record[] mxRecords = new Lookup(host, Type.MX).run();
 3             if(ArrayUtils.isEmpty(mxRecords)) return false;
 4             // 邮件服务器地址
 5             String mxHost = ((MXRecord)mxRecords[0]).getTarget().toString();
 6             if(mxRecords.length > 1) { // 优先级排序
 7                 List<Record> arrRecords = new ArrayList<Record>();
 8                 Collections.addAll(arrRecords, mxRecords);
 9                 Collections.sort(arrRecords, new Comparator<Record>() {
10
11                     public int compare(Record o1, Record o2) {
12                         return new CompareToBuilder().append(((MXRecord)o1).getPriority(), ((MXRecord)o2).getPriority()).toComparison();
13                     }
14
15                 });
16                 mxHost = ((MXRecord)arrRecords.get(0)).getTarget().toString();
17             }

mx

  (上面代码中的生僻类型就是来自dnsjava,我使用apache-commons组件来判断空值和构建排序,return false是在查询失败时。)

  接下来通过优先级排序(mx记录有这个属性)取第一个邮件服务器地址来链接。

  这里的主要代码是通过SMTP发送RCPT TO指令来指定邮件接收方,如果这个地址存在则服务器返回成功状态,如果没有的话则返回错误指令。

  1 import java.io.BufferedInputStream;
  2 import java.io.BufferedReader;
  3 import java.io.BufferedWriter;
  4 import java.io.IOException;
  5 import java.io.InputStreamReader;
  6 import java.io.OutputStreamWriter;
  7 import java.net.InetSocketAddress;
  8 import java.net.Socket;
  9 import java.util.ArrayList;
 10 import java.util.Collections;
 11 import java.util.Comparator;
 12 import java.util.List;
 13
 14 import org.apache.commons.lang.ArrayUtils;
 15 import org.apache.commons.lang.StringUtils;
 16 import org.apache.commons.lang.builder.CompareToBuilder;
 17 import org.xbill.DNS.Lookup;
 18 import org.xbill.DNS.MXRecord;
 19 import org.xbill.DNS.Record;
 20 import org.xbill.DNS.TextParseException;
 21 import org.xbill.DNS.Type;
 22
 23
 24 public class MailValid {
 25
 26     public static void main(String[] args) {
 27         System.out.println(new MailValid().valid("100582783@qq.com", "jootmir.org"));
 28     }
 29
 30     /**
 31      * 验证邮箱是否存在
 32      * <br>
 33      * 由于要读取IO,会造成线程阻塞
 34      *
 35      * @param toMail
 36      *         要验证的邮箱
 37      * @param domain
 38      *         发出验证请求的域名(是当前站点的域名,可以任意指定)
 39      * @return
 40      *         邮箱是否可达
 41      */
 42     public boolean valid(String toMail, String domain) {
 43         if(StringUtils.isBlank(toMail) || StringUtils.isBlank(domain)) return false;
 44         if(!StringUtils.contains(toMail, '@')) return false;
 45         String host = toMail.substring(toMail.indexOf('@') + 1);
 46         if(host.equals(domain)) return false;
 47         Socket socket = new Socket();
 48         try {
 49             // 查找mx记录
 50             Record[] mxRecords = new Lookup(host, Type.MX).run();
 51             if(ArrayUtils.isEmpty(mxRecords)) return false;
 52             // 邮件服务器地址
 53             String mxHost = ((MXRecord)mxRecords[0]).getTarget().toString();
 54             if(mxRecords.length > 1) { // 优先级排序
 55                 List<Record> arrRecords = new ArrayList<Record>();
 56                 Collections.addAll(arrRecords, mxRecords);
 57                 Collections.sort(arrRecords, new Comparator<Record>() {
 58
 59                     public int compare(Record o1, Record o2) {
 60                         return new CompareToBuilder().append(((MXRecord)o1).getPriority(), ((MXRecord)o2).getPriority()).toComparison();
 61                     }
 62
 63                 });
 64                 mxHost = ((MXRecord)arrRecords.get(0)).getTarget().toString();
 65             }
 66             // 开始smtp
 67             socket.connect(new InetSocketAddress(mxHost, 25));
 68             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream())));
 69             BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
 70             // 超时时间(毫秒)
 71             long timeout = 6000;
 72             // 睡眠时间片段(50毫秒)
 73             int sleepSect = 50;
 74
 75             // 连接(服务器是否就绪)
 76             if(getResponseCode(timeout, sleepSect, bufferedReader) != 220) {
 77                 return false;
 78             }
 79
 80             // 握手
 81             bufferedWriter.write("HELO " + domain + "\r\n");
 82             bufferedWriter.flush();
 83             if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
 84                 return false;
 85             }
 86             // 身份
 87             bufferedWriter.write("MAIL FROM: <check@" + domain + ">\r\n");
 88             bufferedWriter.flush();
 89             if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
 90                 return false;
 91             }
 92             // 验证
 93             bufferedWriter.write("RCPT TO: <" + toMail + ">\r\n");
 94             bufferedWriter.flush();
 95             if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
 96                 return false;
 97             }
 98             // 断开
 99             bufferedWriter.write("QUIT\r\n");
100             bufferedWriter.flush();
101             return true;
102         } catch (NumberFormatException e) {
103         } catch (TextParseException e) {
104         } catch (IOException e) {
105         } catch (InterruptedException e) {
106         } finally {
107             try {
108                 socket.close();
109             } catch (IOException e) {
110             }
111         }
112         return false;
113     }
114
115     private int getResponseCode(long timeout, int sleepSect, BufferedReader bufferedReader) throws InterruptedException, NumberFormatException, IOException {
116         int code = 0;
117         for(long i = sleepSect; i < timeout; i += sleepSect) {
118             Thread.sleep(sleepSect);
119             if(bufferedReader.ready()) {
120                 String outline = bufferedReader.readLine();
121                 // FIXME 读完……
122                 while(bufferedReader.ready())
123                     /*System.out.println(*/bufferedReader.readLine()/*)*/;
124                 /*System.out.println(outline);*/
125                 code = Integer.parseInt(outline.substring(0, 3));
126                 break;
127             }
128         }
129         return code;
130     }
131 }

  (解锁和输出123、124行数据可以让你更加清晰SMTP协议)

  对于企业邮箱,可能无法正常验证,这个是因为服务器问题。另外,dnsjava查询DNS是有缓存的。

  现在工作越来越紧张,对于技术的理解也较以前深刻。现在写出的代码可能较为精简(这意味着如果你是新手可能不容易理解)。

联系我,一起交流


欢迎您移步我们的交流群,无聊的时候大家一起打发时间:

或者通过QQ与我联系:

(最后编辑时间2015-04-29 10:27:44)

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

  1. java socket 连接邮箱_Java与邮件系统交互之使用Socket验证邮箱是否存在

    最近遇到一个需求:需要验证用户填写的邮箱地址是否真实存在,是否可达.和普通的正则表达式不同,他要求尝试链接目标邮箱服务器并请求校验目标邮箱是否存在. 先来了解 DNS之MX记录 对于DNS不了解的,请 ...

  2. android java 调用js,Android中Java和JavaScript交互实例

    Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中,JavaScript又是一个很举足轻重的脚本.本文将介绍如何实现Java代码和Javascript代码的相互调用. 如 ...

  3. c java交互,Java与C交互

    /** * Java与C交互 * @param param 上送信息 * @return 返回信息 */ public static Map infExchange(Object param){ Ma ...

  4. Android入门——WebView使用及Java和JavaScript交互小结

    文章大纲 引言 一.WebView 概述 二.WebView的常用的方法 1.构造方法 2.其他常用方法 3.WebSettings常用的方法 三.WebView的应用 1.使用WebView的基本步 ...

  5. c#(服务器)与java(客户端)通过socket传递对象_C#(服務器)與Java(客戶端)通過Socket傳遞對象...

    最近做項目,需要C#與java間的交互,也就是C#編寫服務器,java編寫客戶端,讓兩者進行通信. 通信無非就是互發數據,首選Socket技術,通過TCP協議建立長連接,一般是以字節數組的形式傳遞數據 ...

  6. 转载——Java与WCF交互(一):Java客户端调用WCF服务

    最近开始了解WCF,写了个最简单的Helloworld,想通过java客户端实现通信.没想到以我的基础,居然花了整整两天(当然是工作以外的时间,呵呵),整个过程大费周折,特写下此文,以供有需要的朋友参 ...

  7. Java与WCF交互(一):Java客户端调用WCF服务

    最近开始了解WCF,写了个最简单的Helloworld,想通过java客户端实现通信.没想到以我的基础,居然花了整整两天(当然是工作以外的时间,呵呵),整个过程大费周折,特写下此文,以供有需要的朋友参 ...

  8. java与js交互,相互调用传参

    随着前端技术的发展与H5的广泛使用,移动端采用native+h5的方式越来越多了,对于Android来说就涉及到java与js的交互,相互调用传参等.下面就来看一下java与js交互的简单demo. ...

  9. java网络编程,通过TCP,Socket实现多对一的局域网聊天室

    java网络编程,通过TCP,Socket实现多对一的局域网聊天室 可以实现多个客户端连接服务器,服务器接收到信息就会把信息广播到所有的客户端 这是服务器端的代码 View Code import j ...

最新文章

  1. 从Visual Studio里抓取抽象语法树(AST)
  2. .NET连接SAP系统专题:C#获取RFC中自定义的异常(四)
  3. auto cad 打印颜色变浅_CAD制图软件中如何设置CAD打印样式表(CTB)?
  4. java大文件读,java 读大文件报错
  5. linux下zookeeper启动命令,For Linux Zookeeper客户端命令行操作指令
  6. WaterfallTree(瀑布树) 详细技术分析系列
  7. 超越自我 — 实现人生价值之路
  8. 【转载】GridView自动排序
  9. Mybatis官方网站
  10. 转载 | 年度盘点!Flink 社区全年的精华内容都在这里啦
  11. Trajectory Similarity Join in Spatial Networks
  12. web服务 面试可能会问的问题
  13. 【技术人快报】摩拜单车多地区现Bug+iCloud完成中国本土化落地
  14. 5. 统计学基础1:平均值...四分位数、方差、标准差(均方差)、标准误(标准误差、均方根误差)、 标准分
  15. Flink-flink原理解读
  16. Profiler分析内存抖动,Memory Analyzer(mat)分析内存泄漏(不懂砍我)
  17. r语言拟合MA模型,及时序图,自相关图,偏自相关图
  18. Java代码通过JDBC连接Hiveserver2
  19. php使用单例的场景,php单例形式 运用场景和运用方法_后端开发
  20. SitePoint播客#148:全部在Facebook火车上

热门文章

  1. vector容器与iterator迭代器
  2. 优化网站性能的35条规则
  3. li前面的原点或者方的样式修改html中列表项li所显示的圆点的颜色?,以及相关样式的设定...
  4. 在Linux下安装Apache
  5. 解决git提交敏感信息(回退git版本库到某一个commit)
  6. 云计算背后的秘密(6)-NoSQL数据库的综述
  7. 设为首页加入收藏代码
  8. 《Ext JS 高级程序设计》的目录与样张
  9. 基于形态学操作提取水平和垂直线条(五线谱中音符和乐谱线的分离)
  10. opencv grabcut