2011-06-02 105 views
2

我有一项服务可以发送电子邮件并将出站电子邮件存储在数据库中。 我使用.NET本机Smtp类进行电子邮件传输。如果电子邮件传递失败,我有一个错误标志。.net smtp用SmtpStatusCode发送 - 什么时候应该发生重试?

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

基本上,我想捕捉一个很好的机会可以重新发送电子邮件的例外。我想这只会是网络错误,而不是电子邮件帐户。在SmtpStatusCode的 将指示值得重试:

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

回答

4

我试图解决您是完全相同的问题。我提出的解决方案涉及大约一个小时通过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; 
    } 

让我知道,如果这是有道理的,或者如果你需要看到更多的代码,但它应该是比较明确的从这里开始。

相关问题