SMTP-отправка .Net с SmtpStatusCode - когда следует повторить попытку? - PullRequest
3 голосов
/ 02 июня 2011

У меня есть служба, которая отправляет электронную почту и сохраняет исходящую почту в базе данных. Я использую .NET собственный класс Smtp для передачи электронной почты. У меня есть флаг ошибки, который устанавливается, если сообщение не доставлено.

Мой сервис будет периодически проверять наличие недоставленных сообщений и пытаться повторно отправить. При каких обстоятельствах следует повторить попытку отправить электронное письмо? Я заметил, что даже если адрес электронной почты будет неправильным, он выдаст исключение, но я хочу, чтобы моя служба отбрасывала все недействительные электронные письма, иначе он будет повторяться вечно.

По сути, я хочу поймать исключение, при котором существует большая вероятность того, что письмо может быть повторно доставлено. Я предполагаю, что это будут только сетевые ошибки, а не учетные записи электронной почты. Какой из SmtpStatusCode будет указывать на необходимость повторной попытки:

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

1 Ответ

6 голосов
/ 03 мая 2012

Я пытаюсь решить ту же проблему, что и вы.Решение, которое я нашел, включало около часа заливки RFC2821 и интерпретации его как можно лучше.Если вы заинтересованы, вы можете найти его здесь - http://www.rfc -editor.org / rfc / rfc2821.txt

Большой отрыв от чтения RFC заключается в том, что коды вида4yz указывает временный отрицательный код завершения, который требует повторной попытки.Коды вида 5yz указывают на постоянный отрицательный код завершения, который НЕ следует повторять.

Таким образом, цель состоит в том, чтобы отфильтровать получателей, если повторная попытка оправдана, а какая - нет.Все коды 5yz можно интерпретировать как «не повторять», за исключением 552, которые некоторые серверы могут ошибочно отправлять вместо 452. После этого вам необходимо решить:

  • Является ли это серьезной технической ошибкой (503 BadCommandSequence), которая должна привести к быстрому отказу службы без повторных попыток.

ИЛИ

  • Это отрицательное постоянное завершениекод, который применяется только к одному получателю, и в этом случае вы просто оставляете данного получателя и повторяете операцию отправки.

Таким образом, мой подход такой:

  • Обработка SmtpFailedRecipientsException и SmtpFailedRecipientException.
  • Сохранение всех неудачных получателей в списке.
  • Для каждого неудачного получателя вызовите мой специальный метод 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;
    }

Дайте мне знать, если это имеет смысл или вам нужно увидеть больше кода, но это должно быть относительно ясно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...