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

先来了解


  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(下载),自己写太麻烦。

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

mx

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

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

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

 import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.MXRecord;
import org.xbill.DNS.Record;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type; public class MailValid { public static void main(String[] args) {
System.out.println(new MailValid().valid("100582783@qq.com", "jootmir.org"));
} /**
* 验证邮箱是否存在
* <br>
* 由于要读取IO,会造成线程阻塞
*
* @param toMail
* 要验证的邮箱
* @param domain
* 发出验证请求的域名(是当前站点的域名,可以任意指定)
* @return
* 邮箱是否可达
*/
public boolean valid(String toMail, String domain) {
if(StringUtils.isBlank(toMail) || StringUtils.isBlank(domain)) return false;
if(!StringUtils.contains(toMail, '@')) return false;
String host = toMail.substring(toMail.indexOf('@') + 1);
if(host.equals(domain)) return false;
Socket socket = new Socket();
try {
// 查找mx记录
Record[] mxRecords = new Lookup(host, Type.MX).run();
if(ArrayUtils.isEmpty(mxRecords)) return false;
// 邮件服务器地址
String mxHost = ((MXRecord)mxRecords[0]).getTarget().toString();
if(mxRecords.length > 1) { // 优先级排序
List<Record> arrRecords = new ArrayList<Record>();
Collections.addAll(arrRecords, mxRecords);
Collections.sort(arrRecords, new Comparator<Record>() { public int compare(Record o1, Record o2) {
return new CompareToBuilder().append(((MXRecord)o1).getPriority(), ((MXRecord)o2).getPriority()).toComparison();
} });
mxHost = ((MXRecord)arrRecords.get(0)).getTarget().toString();
}
// 开始smtp
socket.connect(new InetSocketAddress(mxHost, 25));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(socket.getInputStream())));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 超时时间(毫秒)
long timeout = 6000;
// 睡眠时间片段(50毫秒)
int sleepSect = 50; // 连接(服务器是否就绪)
if(getResponseCode(timeout, sleepSect, bufferedReader) != 220) {
return false;
} // 握手
bufferedWriter.write("HELO " + domain + "\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 身份
bufferedWriter.write("MAIL FROM: <check@" + domain + ">\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 验证
bufferedWriter.write("RCPT TO: <" + toMail + ">\r\n");
bufferedWriter.flush();
if(getResponseCode(timeout, sleepSect, bufferedReader) != 250) {
return false;
}
// 断开
bufferedWriter.write("QUIT\r\n");
bufferedWriter.flush();
return true;
} catch (NumberFormatException e) {
} catch (TextParseException e) {
} catch (IOException e) {
} catch (InterruptedException e) {
} finally {
try {
socket.close();
} catch (IOException e) {
}
}
return false;
} private int getResponseCode(long timeout, int sleepSect, BufferedReader bufferedReader) throws InterruptedException, NumberFormatException, IOException {
int code = 0;
for(long i = sleepSect; i < timeout; i += sleepSect) {
Thread.sleep(sleepSect);
if(bufferedReader.ready()) {
String outline = bufferedReader.readLine();
// FIXME 读完……
while(bufferedReader.ready())
/*System.out.println(*/bufferedReader.readLine()/*)*/;
/*System.out.println(outline);*/
code = Integer.parseInt(outline.substring(0, 3));
break;
}
}
return code;
}
}

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

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

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

联系我,一起交流


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

或者通过QQ与我联系:

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

Java与邮件系统交互之使用Socket验证邮箱是否存在的更多相关文章

  1. Android 中Java和JavaScript交互入门

    如何实现JavaScript 和java 交互 实现Java和js交互十分便捷.通常只需要以下几步. WebView开启JavaScript脚本执行 WebView设置供JavaScript调用的交互 ...

  2. Android中Java和JavaScript交互

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

  3. Java与WCF交互(二):WCF客户端调用Java web service【转】

    原文:http://www.cnblogs.com/downmoon/archive/2010/08/25/1807982.html 在上篇< Java与WCF交互(一):Java客户端调用WC ...

  4. 转载——Java与WCF交互(二):WCF客户端调用Java Web Service

    在上篇< Java与WCF交互(一):Java客户端调用WCF服务>中,我介绍了自己如何使用axis2生成java客户端的悲惨经历.有同学问起使用什么协议,经初步验证,发现只有wsHttp ...

  5. Android WebView加载本地html并实现Java与JS交互

    最近做的一个项目中,用到自定义地图,将自定义地图转换成html页面,现在需要做的是如何将本地的html加载到android中,并可以实现交互. 相关讲解: 其实webview加载资源的速度并不慢,但是 ...

  6. java的nio之:java的bio流下实现的socket服务器同步阻塞模型和socket的伪异步的socket服务器的通信模型

    同步I/O模型的弊端===>每一个线程的创建都会消耗服务端内存,当大量请求进来,会耗尽内存,导致服务宕机 伪异步I/O的弊端分析===>当对Socket的输入流进行读取操作的时候,它会一直 ...

  7. Java与WCF交互(一)补充:用WSImport生成WSDL的Java客户端代码

    在<Java与WCF交互(一):Java客户端调用WCF服务>一 文中,我描述了用axis2的一个Eclipse控件生成WCF的Java客户端代理类,后来有朋友建议用Xfire.CXF,一 ...

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

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

  9. java程序连接oracle12c报:java.sql.SQLException: ORA-28040: 没有匹配的验证协议。

    报错信息: 2017-09-22 15:17:37,204 WARN [org.hibernate.cfg.SettingsFactory] - Could not obtain connection ...

随机推荐

  1. azure之MSSQL服务性能测试

    azure给我们提供非常多的服务而这些服务可以让企业轻而易举地在上构建一个健壮的服务体系.但在使用azure的相关产品服务时还是应该对相关的服务有一些简单的性能了解才能更好的让企业购买适合自己的服务产 ...

  2. 【基础知识】.Net基础加强11天

    一. 扩展方法 1. 声明扩展方法的步骤: 1> 类必须是static,方法是static ,第一个参数是被扩展的对象,前面标注(this 数据类型参数名). 2> 使用扩展方法的时候必须 ...

  3. Asp.Net Web API 2第十五课——Model Validation(模型验证)

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文参考链接文章地址htt ...

  4. Linux:Shell 常用通配符

    字符 含义 * 匹配 0 或多个字符 ? 匹配任意一个字符 [list] 匹配 list 中的任意单一字符 [!list] 匹配 除list 中的任意单一字符以外的字符 [c1-c2] 匹配 c1-c ...

  5. iOS企业级开发

    2015移动技术白皮书 Android篇 iOS篇 项目管理篇 综合篇 结束语 iOS项目框架设计 项目结构的设计 基类的设计 自定义生命周期 跳转器 自定义UV打点控件 图片缓存 iOS网络底层框架 ...

  6. 如何带领一个Android开发团队

    1)重构,夜未眠 将框架从业务中剥离 如何提高开发效率 如何提高程序性能 单元测试 技术调研 代码版本管理 2)渠道包管理 自动打包工具 批量打渠道包的两种解决方案 定制渠道包的流程管理 3)稳定性, ...

  7. [BTS] The adapter "SQL" raised an error message. Details "The Messaging Engine is shutting down. ".

    Get a warning in event log. Log Name:      ApplicationSource:        BizTalk ServerDate:          3/ ...

  8. Android Application 对象介绍

    What is Application Application和Actovotu,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application ...

  9. STL的string和wstring

    STL有字符串处理类——stirng和wstring,但是用的时候会觉得不是很方便,因为它不能像TCHAR一样根据定义的宏在char类型字符串和wchar_t进行转换,总不能因为程序要Unicode就 ...

  10. 通过rinetd实现端口转发来访问内网的服务

    通过rinetd实现端口转发来访问内网的服务 一.   问题描述 通过外网来访问内网的服务 二.   环境要求 需要有一台能够外网访问的机器做端口映射,通过数据包转发来实现外部访问阿里云的内网服务 三 ...