.Net smtp 使用 SmtpStatusCode 发送 - 何时应重试?

发布于 2024-11-13 07:03:41 字数 458 浏览 1 评论 0原文

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

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

基本上,我想捕获异常,即很有可能重新发送电子邮件。我想这只是网络错误而不是电子邮件帐户。 哪个 SmtpStatusCode 表明值得重试:

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

I have a service which sends email and stores the outbound email in a database.
I'm using .NET native Smtp class for email transmission. I have an error flag which is set if the email failed delivery.

My service will periodically check for undelivered messages and try to re-send. Under what circumstances should it retry to send the email? I've noted that even if the email address is incorrect it will throw an exception, but I want my service to ditch any invalid emails otherwise it will retry forever.

Basically, I want to catch the exception whereby there is a good chance the email can be re-delivered. I guess this would be only network errors rather than email accounts.
Which of the SmtpStatusCode would indicate worthy of retry:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

巡山小妖精 2024-11-20 07:03:41

我正在尝试解决与您完全相同的问题。我想出的解决方案涉及大约一个小时的时间来研究 RFC2821 并尽我所能地解释它。如果您有兴趣,可以在这里找到它 - http://www.rfc-editor.org /rfc/rfc2821.txt

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

因此,目标是过滤掉需要重试和不需要重试的收件人。所有 5yz 代码都可以解释为“不要重试”,但 552 除外,某些服务器可能会错误地发送 552,而不是 452。从那时起,您需要决定:

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

或者

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

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

  • 处理 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;
    }

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

I'm trying to solve the exact same problem you are. The solution I came up involved about an hour of pouring through RFC2821 and interpreting it as best as I could. If you are interested you can find it here - http://www.rfc-editor.org/rfc/rfc2821.txt

The big take-away from reading the RFC is that codes of the form 4yz indicate a transient negative completion code, which warrants a retry. Codes of the form 5yz indicate a permanent negative completion code, which you should NOT retry.

So the goal is to filter out recipients where a retry is warranted and where it isn't. All of the 5yz codes can be interpreted as "don't retry" with the exception of 552, which some servers may erroneously send instead of 452. From there on out you need to decide:

  • Is this a serious technical error (503 BadCommandSequence) which should cause your service to fail fast without retrying anything.

OR

  • Is this a negative permanent completion code which only applies to a single recipient, in which case you just leave out the recipient in question and retry the send operation.

Thus, my approach is such:

  • Handle the SmtpFailedRecipientsException and SmtpFailedRecipientException.
  • Store all failed recipients into a list.
  • For each failed recipient, call my special HandleFailedRecipient() method which interprets the SMTP code. If at any time that method returns false then this indicates a much more serious error and my service should just fail fast (without trying to resend the email).
  • Otherwise, the method may or may not re-add the recipient (depending on the SMTP code).
  • Finally, if the message still has any remaining recipients, try to resend.

Here is my method which interprets the SMTP code:

    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;
    }

Let me know if this makes sense or if you need to see more code, but it should be relatively clear-cut from here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文