Почему SmtpClient.SendAsync вызывается только один раз?
Я пытаюсь написать службу уведомлений (для полностью законных целей, не связанных со спамом) в .NET с использованием SmtpClient. Вначале я просто зацикливал каждое сообщение и отправлял его, однако это медленно, и я хотел бы улучшить скорость. Итак, я переключился на "SendAsync", но теперь получаю следующую ошибку при втором вызове:
An asynchronous call is already in progress.
Я прочитал это, чтобы означать, что MS искалечила System.Net.Mail, чтобы предотвратить массовые рассылки. Это верно? Если это так, есть лучший способ сделать это в .NET и все еще иметь возможность регистрировать результаты каждого электронного письма (что важно для нашего клиента). Если нет, то почему SendAsync можно вызывать только один раз?
Ответы
Ответ 1
В соответствии с документацией:
После вызова SendAsync вы должны подождать для передачи электронной почты на завершить, прежде чем пытаться отправить другое электронное сообщение с использованием Отправить или SendAsync.
Таким образом, чтобы отправлять несколько писем одновременно, вам нужно несколько экземпляров SmtpClient.
Ответ 2
Возможно, вы сможете использовать следующее:
ThreadPool.QueueUserWorkItem(state => client.Send(msg));
Это должно позволять отправлять ваши сообщения в очередь и отправлять их по мере того, как потоки становятся доступными.
Ответ 3
Очевидно, что это не попытка остановить массовые почтовые программы.
Причина в том, что класс SmtpClient не является потокобезопасным. Если вы хотите отправлять несколько электронных писем одновременно, вам нужно создать несколько рабочих потоков (в .NET Framework есть несколько способов сделать это) и создать отдельные экземпляры SmtpClient в каждом из них.
Ответ 4
Я думаю, вы неправильно поняли класс методов XXXAsync
. Цель этих асинхронных вызовов состоит в том, чтобы позволить программе продолжать работу без необходимости завершения обработки и возврата метода. Затем вы можете продолжить результат позже, подписавшись на что-то вроде события XXXReceived
объекта.
Чтобы отправить несколько сообщений одновременно, вы можете использовать более Thread
s.
Ответ 5
Как заметили все остальные здесь, вы можете отправлять только одно электронное сообщение за раз, но способ отправки другого после первого отправления - это обработать событие .SendCompleted класса SmtpClient, а затем перейти к следующее письмо и отправьте это.
Если вы хотите отправить много писем одновременно, то, как сказали другие, используйте несколько объектов SmtpClient.
Ответ 6
Вы можете отправлять только по одному за SMTP-клиентом. Если вы хотите сделать несколько вызовов для отправки, создайте несколько клиентов SMTP.
НТН,
Колби Африка
Ответ 7
Существует причина повторного использования SmtpClient, он ограничивает количество подключений к SMTP-серверу. Я не могу создать экземпляр класса класса SmtpClient для каждого потока, на котором создаются отчеты, или на SMTP-сервер будет ошибка со слишком большим количеством ошибок соединений. Это решение, которое я придумал, когда я не мог найти ответ здесь.
Я закончил использование AutoResetEvent для хранения всего в синхронизации. Таким образом, я могу продолжать звонить в SendAsync в каждом потоке, но дождаться его обработки электронной почты и использования события SendComplete для его сброса, чтобы следующий был продолжен.
Я настраиваю событие автоматического сброса.
AutoResetEvent _autoResetEvent = new AutoResetEvent(true);
Я настраиваю общий SMTP-клиент при создании экземпляра класса.
_smtpServer = new SmtpClient(_mailServer);
_smtpServer.Port = Convert.ToInt32(_mailPort);
_smtpServer.UseDefaultCredentials = false;
_smtpServer.Credentials = new System.Net.NetworkCredential(_mailUser, _mailPassword);
_smtpServer.EnableSsl = true;
_smtpServer.SendCompleted += SmtpServer_SendCompleted;
Затем, когда я вызываю async send, я жду, когда событие очистится, а затем отправьте следующий.
_autoResetEvent.WaitOne();
_smtpServer.SendAsync(mail, mail);
mailWaiting++;
Я использую событие SMTPClient SendComplete для сброса AutoResetEvent, чтобы отправить следующее электронное письмо.
private static void SmtpServer_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
MailMessage thisMesage = (MailMessage) e.UserState;
if (e.Error != null)
{
if (e.Error.InnerException != null)
{
writeMessage("ERROR: Sending Mail: " + thisMesage.Subject + " Msg: "
+ e.Error.Message + e.Error.InnerException.Message);
}
else
{
writeMessage("ERROR: Sending Mail: " + thisMesage.Subject + " Msg: " + e.Error.Message);
}
}
else
{
writeMessage("Success:" + thisMesage.Subject + " sent.");
}
if (_messagesPerConnection > 20)
{ /*Limit # of messages per connection,
After send then reset the SmtpClient before next thread release*/
_smtpServer = new SmtpClient(_mailServer);
_smtpServer.SendCompleted += SmtpServer_SendCompleted;
_smtpServer.Port = Convert.ToInt32(_mailPort);
_smtpServer.UseDefaultCredentials = false;
_smtpServer.Credentials = new NetworkCredential(_mailUser, _mailPassword);
_smtpServer.EnableSsl = true;
_messagesPerConnection = 0;
}
_autoResetEvent.Set();//Here is the event reset
mailWaiting--;
}