Java与邮件系统交互之使用Socket验证邮箱是否存在详解编程语言

Java与邮件系统交互之使用Socket验证邮箱是否存在详解编程语言

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

先来了解

DNS之MX记录

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

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

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

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

SMTP之纯Socket访问

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

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

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

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

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

(上面代码中的生僻类型就是来自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(”
[email protected]“, ”
[email protected]“));

    }

    /**

     * 验证邮箱是否存在

     * 由于要读取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: <
[email protected]” + 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 (Exception e) {

                e.printStackTrace();

        } finally {

            try {

                socket.close();

            } catch (IOException e) {

                    e.printStackTrace();

            }

        }

        return false;

    }

    

    private int getResponseCode(long timeout, int sleepSect, BufferedReader br) throws Exception {

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

                code = Integer.parseInt(outline.substring(0, 3));

                break;

            }

        }

        return code;

    }

}

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

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

Java与邮件系统交互之使用Socket验证邮箱是否存在详解编程语言

转载请注明来源网站:blog.ytso.com谢谢!

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/14713.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论