Почему SmtpClient не работает с несколькими различными исключениями при использовании SSL?

Я пытаюсь отправить почтовое сообщение, используя System.Net.Mail.SmtpClient с включенным SSL. Без изменения внешних факторов (т.е. нажмите F5, затем снова нажмите F5 - или даже в цикле) это работает несколько раз, но в большинстве случаев не работает.

Пример кода:

public void SendMail()
        {
            using (var client = new SmtpClient())
            {
                MailMessage mailMessage = new MailMessage();
                client.EnableSsl = true;
                client.Host = "smtp.example.com";
                client.Timeout = 10000;
                client.DeliveryMethod = SmtpDeliveryMethod.Network;
                mailMessage.From = new MailAddress("[email protected]");
                mailMessage.To.Add(new MailAddress("[email protected]"));
                mailMessage.Subject = "Test";
                mailMessage.Body = "Message " + DateTime.Now.ToString();
                try
                {
                    client.Send(mailMessage);
                }
                catch (Exception ex)
                {
                    // This being a Pokemon block is besides the point
                }
            }
        }

В моем блоке catch я просто получаю тайм-аут, но если я настрою трассировку для System.Net и System.Net.Sockets, я увижу ряд различных исключений:

  • Не удалось прочитать данные из транспортного соединения: операция блокировки была прервана вызовом WSACancelBlockingCall.
  • Невозможно прочитать данные из транспортного соединения: установленное соединение было прервано программным обеспечением на вашем хост-компьютере.
  • Не удается получить доступ к удаленному объекту
  • Чтение не поддерживается в этом потоке
  • Запись не поддерживается в этом потоке

Все это происходит после EHLO, STARTTLS и аутентификации. Клиент и сервер общаются друг с другом своими криптографическими способами, когда выбрасываются исключения.

System.Net.Mail.SmtpClient.Send() — это единственный кадр, общий для всех стеков вызовов.

Конечно, я пытался изменить код разными способами, например, установить client.UseDefaultCredentials, использовать client.SendAsync() и так далее. Но я подозреваю, что это что-то совершенно другое, прерывающее соединение в разное время - возможно, ошибка конфигурации на одной из задействованных машин?

Итак, я проверил журналы событий на наличие признаков того, что что-то мешает соединению, но ничего не нашел.

Любые предложения относительно того, что я должен искать здесь?


person Torben Frandsen    schedule 10.02.2016    source источник
comment
Вы действительно ожидаете найти SMTP-сервер, расположенный по адресу smtp.example.com???   -  person JamieMeyer    schedule 10.02.2016
comment
@JamieMeyer, вы хотите, чтобы OP предоставил все подробности о своем SMTP-сервере для всеобщего ознакомления?   -  person user1666620    schedule 10.02.2016
comment
Я не думаю, что проблема связана с кодом, который вы разместили, а с чем-то еще в вашем приложении. Можете ли вы показать код, который вызывает ваш метод SendMail()? Вы установили точки останова, чтобы увидеть, где именно возникает исключение и где оно перехватывается? Кроме того, может ли кто-нибудь объяснить отрицательный голос?   -  person user1666620    schedule 10.02.2016
comment
Конечно, я отредактировал имена серверов и т. д. Я также не всегда хочу отправлять свои письма на адрес [email protected] :)   -  person Torben Frandsen    schedule 10.02.2016
comment
Больше нечего показывать, кроме вызова этого метода. Этот код был изолирован в небольшом консольном приложении, чтобы исключить источники ошибок.   -  person Torben Frandsen    schedule 10.02.2016
comment
Проверьте это и посмотрите, помогут ли они: stackoverflow.com/questions/5791453/ stackoverflow.com/questions/17497154/ social.msdn.microsoft.com/Forums/en-US/   -  person user1666620    schedule 10.02.2016


Ответы (3)


Иногда тайм-аут - это просто тайм-аут...

Углубившись в эту проблему, я использовал Wireshark, чтобы увидеть, что происходит на сетевом уровне.

Когда случались сбои, клиентская машина неизменно отправляла пакет TCP [RST, ACK] примерно через 10 секунд после EHLO. Промежуток времени заметно близок к моему значению тайм-аута. Когда я удвоил значение тайм-аута, письма каждый раз проходили без сбоев.

Конечно, если бы мы тратили более 10 секунд на отправку каждого письма, мы бы вскоре часами стояли в очереди на почту в нашей производственной системе, поэтому мне нужно было выяснить, почему это заняло так много времени.

Мое внимание привлекла одна деталь: Трассировка Wireshark

Клиент тратил более четырех секунд между двумя пакетами. И обычно между пакетами тратились сотни миллисекунд, тогда как сервер отвечал в течение десятков.

Следующим шагом было создание кода без символов отладки и повторное измерение без подключенного отладчика. Время, потраченное на одно отправленное электронное письмо, сразу сократилось до доли секунды. Теперь для меня не новость, что код работает медленнее с подключенным отладчиком, но то, что он работает намного медленнее, было неожиданностью.

Итак, уроки, которые я должен был усвоить, были следующими: старайтесь не слишком анализировать, а когда дело касается времени, отключайте отладчик время от времени.

person Torben Frandsen    schedule 11.02.2016

Это должен быть комментарий (у меня нет репутации, чтобы делать комментарии), но это может быть проблема с брандмауэром, поскольку вы получаете сообщение «Невозможно прочитать данные из транспортного соединения: установленное соединение было прервано программным обеспечением в ошибка вашего хост-компьютера (файрвол или антивирус отменили вашу попытку отправить письмо).

Ошибка «Невозможно прочитать данные из транспортного соединения: операция блокировки была прервана вызовом WSACancelBlockingCall» возникает при закрытии потока — см. этот вопрос - и другие ошибки, которые вы перечислили, похоже, связаны с попыткой доступа поток, когда он закрыт, тоже.

Все эти ошибки указывают на общую причину: разрыв соединения где-то между вами и сервером и, скорее всего, на вашем компьютере/сети, и вы получаете разные ошибки в зависимости от того, в какой момент эффект распространяется на ваше приложение.

Другое дело, если вы используете

Credentials = new NetworkCredential(fromAddress.Address, fromPassword)

тогда вы должны включить ssl после, а не раньше, иначе он будет очищен.

person secret squirrel    schedule 10.02.2016

Я полагаю, вы пропустили установку свойства Credentials вашего хоста. Однако другой причиной исключений может быть неправильное имя хоста и порт. Попробуйте что-то вроде:

    public void SendMail()
    {
        try
        {
            var fromAddress = new MailAddress("[email protected]", "Some text");
            var toAddress = new MailAddress("[email protected]");
            const string fromPassword = "from account password";

            var smtp = new SmtpClient
            {
                Host = "smtp.example.com",
                Port = "your host port", //for gmail is 587
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
            };
            using (var message = new MailMessage(fromAddress, toAddress)
            {
                Subject = "Test",
                Body = "Message " + DateTime.Now.ToString()
            })
            {
                smtp.Send(message);
            }
        }
        catch (Exception ex)
        {
            //do something
        }
   }

Он протестирован с хостом Gmail, который smtp.gmail.com, и он работает правильно.

person yanis    schedule 10.02.2016
comment
Это плохой ответ, поскольку он не объясняет, что не так с кодом ОП, почему ОП получает ошибку или почему этот код лучше, чем код, опубликованный ОП. На самом деле похоже, что вы вообще не читали вопрос ОП. - person user1666620; 10.02.2016
comment
Ваши изменения по-прежнему недостаточны, так как сведения о SMTP-соединении также можно определить в файле .config приложения. Кроме того, сообщения об ошибках, предоставленные OP, не указывают на ошибку аутентификации клиента. Как объясняет ОП, иногда электронные письма отправляются правильно, иногда бывают исключения. - person user1666620; 10.02.2016
comment
Я отредактировал сообщение, чтобы объяснить, что я думаю о данной информации. - person yanis; 10.02.2016
comment
Как неправильные учетные данные объясняют ошибку Невозможно прочитать данные из транспортного соединения: операция блокировки была прервана вызовом WSACancelBlockingCall? - person user1666620; 10.02.2016
comment
Как видите, есть еще одно предположение для задач. Конечно, если я узнаю больше о проблеме, я отредактирую пост. - person yanis; 10.02.2016