17

我需要发送数百份时事通讯,但想先检查服务器上是否存在电子邮件。它被称为SMTP验证,至少我是这样认为的,基于我对 Internet 的研究。

有几个库可以做到这一点,还有一个ASP Classic中的开源代码页面(http://www.coveryourasp.com/ValidateEmail.asp#Result3),但我很难阅读 ASP Classic,而且看起来它使用了一些第三方库......

是否有一些 C# 中的 SMTP 验证代码,和/或它如何工作的一般解释?

4

7 回答 7

20

请注意,大多数 MTA(邮件传输代理)出于垃圾邮件保护的原因会关闭 VRFY 命令,如果您连续尝试多个 RCPT TO,它们甚至可能会阻止您(请参阅http://www.spamresource.com/ 2007/01/whatever-happened-to-vrfy.html)。因此,即使您找到了一个库来进行验证,它也不会很值钱。Ishmaeel 是对的,真正找出答案的唯一方法是发送一封电子邮件,看看它是否被退回。

@Hrvoje:是的,我建议您监控被拒绝的电子邮件。但是:并非所有退回的邮件都会自动出现在您的“不存在”列表中,您还必须区分临时(例如邮箱已满)和永久错误。

于 2008-08-26T06:47:39.843 回答
10

SMTP 是基于 TCP/IP 的基于文本的协议。

您的验证程序需要打开到服务器端口 25 (SMTP) 的 TCP/IP 连接,写几行代码并阅读答案。验证在“RCTP TO”行和“VFRY”行上完成(但不总是)。

SMTP RFC描述了它 是如何工作的(参见下面的 Green@Beta.ARPA,S 是客户端发送的行,R 是从服务器接收的行):

SMTP 程序示例

         这个 SMTP 示例显示了 Smith 在主机 Alpha.ARPA 上发送的邮件,
         给主机 Beta.ARPA 的琼斯、格林和布朗。这里我们假设
         该主机 Alpha 直接联系主机 Beta。

            S: 邮件来自:
            回复:250 好

            S:RCPT 至:
            回复:250 好

            S:RCPT 至:
            R: 550 这里没有这样的用户
于 2008-08-26T06:33:48.533 回答
4

尽管许多域确实会因为滥用而返回误报,但仍然有一些很棒的组件可以执行除了 SMTP 验证之外的多个级别的验证。例如,值得先检查一下是否至少存在该域。我正在编译我自己的与此问题相关的资源列表,您可以在此处跟踪:

http://delicious.com/dworthley/email.validation(断开的链接)

对于那些可能想要添加到此列表中的人,我还将在此处包括我目前拥有的内容:

对于防弹表单和出色的用户体验,验证电子邮件地址的尽可能多的方面很有帮助。我可以从他们检查的aspNetMX验证器中看到:

  • 语法
  • 针对错误电子邮件地址列表的电子邮件
  • 针对坏域列表的域
  • 邮箱域列表
  • 域是否存在
  • 域是否有 MX 记录
  • 最后通过 SMTP 是否存在邮箱

管理员可以通过对基本上所有帐户验证请求返回 true 来绕过最后一步,但在大多数情况下,如果用户故意输入了错误的地址,则它已经被抓住了。如果地址的域部分是用户错误,也会被捕获。

当然,将此类服务用于注册屏幕或表单的最佳实践是将此类验证与验证过程相结合,以确保电子邮件地址有效。在验证过程之前使用电子邮件验证器的好处在于,它将带来更好的整体用户体验。

于 2008-11-14T18:57:06.003 回答
3

您可以尝试以下代码,它对我来说很好:

public class EmailTest {
    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;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    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;
    }

    @SuppressWarnings("rawtypes")
    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[] = { "rahul.saraswat@techblue.com", "rahul.saraswat@techblue.co.uk", "srswt.rahul12345@gmail.com",
        "srswt.rahul@gmail.com" };
        System.out.println(testData.length);
        for (int ctr = 0; ctr < testData.length; ctr++) {
            System.out.println(testData[ctr] + " is valid? " + isAddressValid(testData[ctr]));
        }
        return;
    }
}

感谢和问候拉胡尔·萨拉斯瓦特

于 2014-02-18T09:21:23.990 回答
2

Real(TM) 电子邮件验证正在尝试向该地址发送一些内容,并查看它是否被拒绝/退回。因此,您只需将它们发送出去,并从您的邮件列表中删除失败的地址。

于 2008-08-26T06:19:36.167 回答
2

不要采取错误的方式,但如今向少数人发送新闻通讯是一件相当严重的事情。是的,您需要监控可能在 SMTP 发送期间同步发生的退回(被拒绝的电子邮件)(通常如果您连接到的 SMTP 服务器是权威的),或者作为系统生成的电子邮件消息异步发生,在一段时间后发生SMTP 发送成功。

发送这些电子邮件时,请牢记 CAN-SPAM 法案并遵守法律;您必须提供取消订阅链接以及实际街道地址(以识别您和 t0 允许用户通过蜗牛邮件发送取消订阅请求,如果他们愿意)。

如果不做这些事情,充其量可能会使您的 IP 被空路由,最坏的情况是被起诉。

于 2008-08-26T08:31:51.527 回答
1

您可能需要这个用于 .NET 的电子邮件验证器组件

这是代码示例:


   // Create a new instance of the EmailValidator class.
   EmailValidator em = new EmailValidator();
   em.MessageLogging += em_MessageLogging;
   em.EmailValidated += em_EmailValidationCompleted;
   try
   {
       string[] list = new string[3] { "test1@testdomain.com", "test2@testdomain.com", "test3@testdomain.com" };
       em.ValidateEmails(list);
   }
   catch (EmailValidatorException exc2)
   {
       Console.WriteLine("EmailValidatorException: " + exc2.Message);
   }
于 2010-05-08T22:22:51.540 回答