Почему SmtpClient.SendAsync вызывается только один раз?

Я пытаюсь написать службу уведомлений (для полностью законных целей без спама) в .NET с помощью SmtpClient. Сначала я просто прокручивал каждое сообщение и отправлял его, однако это медленно, и я хотел бы улучшить скорость. Итак, я переключился на использование "SendAsync", но теперь получаю следующую ошибку при втором вызове:

An asynchronous call is already in progress. 

Я прочитал это, чтобы означать, что MS искалечена система.Сеть.Почта для предотвращения массовых рассылок. Правильно ли это? Если так, есть ли лучший способ сделать это в .NET, и по-прежнему иметь возможность регистрировать результаты каждого письма(что важно для нашего клиента). Если нет, то почему SendAsync можно вызвать только один раз?

7 ответов


по словам документация:

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

поэтому для отправки нескольких писем одновременно вам нужно несколько экземпляров SmtpClient.


вы можете использовать следующее:

ThreadPool.QueueUserWorkItem(state => client.Send(msg));

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


очевидно, что это не попытка остановить массовые рассылки.

причина в том, что класс SmtpClient не является потокобезопасным. Если вы хотите отправить несколько писем одновременно, вам нужно создать несколько рабочих потоков (есть несколько способов сделать это в .NET Framework) и создать отдельные экземпляры SmtpClient в каждом из них.


Я думаю, вы неправильно поняли XXXAsync класса методов. Цель этих асинхронных вызовов состоит в том, чтобы позволить программе продолжить работу без необходимости метода для завершения обработки и возврата в первую очередь. Затем вы можете продолжить результат позже, подписавшись на что-то вроде XXXReceived событие объекта.

чтобы отправить более одного письма одновременно, вы можете использовать больше Threads.


Как заметили все остальные здесь, вы можете отправлять только одно письмо за раз, но способ отправить другое, как только первое было отправлено, - это обработать .Sendcompleted событие класса SmtpClient, а затем перейти к следующему письму и отправить его.

Если вы хотите отправить много писем одновременно, то, как говорили другие, используйте несколько объектов SmtpClient.


вы можете отправлять только по одному на SMTP-клиент. Если вы хотите сделать несколько вызовов отправки, создайте несколько SMTP-клиентов.

HTH,

Колби Африке


есть причина для повторного использования 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;

затем, когда я вызываю send async, я жду, пока событие очистится, а затем отправлю следующий.

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