.Net smtp与SmtpStatusCode一起发送 - 何时应该重试?

时间:2011-06-02 11:09:17

标签: .net smtp

我有一项服务,可以发送电子邮件并将出站电子邮件存储在数据库中。 我正在使用.NET本机Smtp类进行电子邮件传输。如果电子邮件发送失败,我会设置一个错误标志。

我的服务会定期检查未送达的邮件并尝试重新发送。在什么情况下它应该重新发送电子邮件?我已经注意到即使电子邮件地址不正确也会引发异常,但我希望我的服务能够丢弃任何无效的电子邮件,否则它将永远重试。

基本上,我想抓住一个例外,即电子邮件很有可能被重新发送。我想这只是网络错误而不是电子邮件帐户。 哪个SmtpStatusCode表示值得重试:

http://msdn.microsoft.com/en-us/library/system.net.mail.smtpstatuscode.aspx

1 个答案:

答案 0 :(得分:6)

我试图解决你遇到的完全相同的问题。我提出的解决方案涉及到一小时的RFC2821倾注并尽可能地解释它。如果您有兴趣,可以在此处找到 - http://www.rfc-editor.org/rfc/rfc2821.txt

阅读RFC的最大好处是,4yz形式的代码表示短暂的否定完成代码,需要重试。 5yz形式的代码表示永久否定完成代码,您不应该重试。

因此,我们的目标是过滤掉需要重试的收件人以及不重审的收件人。所有5yz代码都可以解释为"不要重试"除了552,有些服务器可能会错误地发送而不是452.从那时起你需要决定:

  • 这是一个严重的技术错误(503 BadCommandSequence),这会导致您的服务快速失败而不会重试任何内容。

OR

  • 这是一个负的永久完成代码,仅适用于单个收件人,在这种情况下,您只需省略相关收件人并重试发送操作。

因此,我的方法是这样的:

  • 处理SmtpFailedRecipientsExceptionSmtpFailedRecipientException
  • 将所有失败的收件人存储到列表中。
  • 对于每个失败的收件人,请调用我的特殊HandleFailedRecipient()方法来解释SMTP代码。如果该方法在任何时候返回false,则表示更严重的错误,我的服务应该快速失败(不尝试重新发送电子邮件)。
  • 否则,该方法可能会也可能不会重新添加收件人(具体取决于SMTP代码)。
  • 最后,如果邮件仍有剩余的收件人,请尝试重新发送。

这是解释SMTP代码的方法:

    private bool HandleFailedRecipient(MailMessage message, string emailAddress, SmtpStatusCode statusCode, AddressType addressType)
    {            
        //Notify any event subscribers that a recipient failed and give them the SMTP code.
        RecipientFailedOnSend(this, new MailAddressEventArgs() { Address = emailAddress, AddressType = addressType, StatusCode = statusCode });

        //5yz codes typically indicate a 'Permanent Negative Completion reply', which means we should NOT keep trying to send the message.
        //The codes below can be interpreted as exceptions to the rule. In these cases we will just strip the user and try to resend.
        if (statusCode == SmtpStatusCode.MailboxUnavailable ||              //550 = "No such user"
            statusCode == SmtpStatusCode.MailboxNameNotAllowed ||           //553 = "User name ambiguous"
            statusCode == SmtpStatusCode.UserNotLocalTryAlternatePath ||    //551 = "Mail address not deliverable"
            statusCode == SmtpStatusCode.TransactionFailed)                 //554 = "Transaction failed / no valid recipients"
        {
            return true;
        }

        //The 4yz codes are 'Transient Negative Completion reply' codes, which means we should re-add the recipient and let the calling routine try again after a timeout.
        if (statusCode == SmtpStatusCode.ServiceNotAvailable ||
            statusCode == SmtpStatusCode.MailboxBusy ||
            statusCode == SmtpStatusCode.LocalErrorInProcessing ||
            statusCode == SmtpStatusCode.InsufficientStorage ||
            statusCode == SmtpStatusCode.ClientNotPermitted ||
//The ones below are 'Positive Completion reply' 2yz codes. Not likely to occur in this scenario but we will account for them anyway.
            statusCode == SmtpStatusCode.SystemStatus ||
            statusCode == SmtpStatusCode.HelpMessage ||
            statusCode == SmtpStatusCode.ServiceReady ||
            statusCode == SmtpStatusCode.ServiceClosingTransmissionChannel ||
            statusCode == SmtpStatusCode.Ok ||
            statusCode == SmtpStatusCode.UserNotLocalWillForward ||
            statusCode == SmtpStatusCode.CannotVerifyUserWillAttemptDelivery ||
            statusCode == SmtpStatusCode.StartMailInput ||
            statusCode == SmtpStatusCode.CannotVerifyUserWillAttemptDelivery ||
            //The code below (552) may be sent by some incorrect server implementations instead of 452 (InsufficientStorage).
            statusCode == SmtpStatusCode.ExceededStorageAllocation)
        {
            if ((addressType & AddressType.To) != 0)
            {
                message.To.Add(emailAddress);
            }
            if ((addressType & AddressType.CC) != 0)
            {
                message.CC.Add(emailAddress);
            }
            if ((addressType & AddressType.BCC) != 0)
            {
                message.Bcc.Add(emailAddress);
            }
            return true;
        }

        //Anything else indicates a very serious error (probably of the 5yz variety that we haven't handled yet). Tell the calling routine to fail fast.
        return false;
    }

让我知道这是否有意义,或者您是否需要查看更多代码,但它应该从这里相对清晰。