Подтвердить что ты не робот

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

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

An asynchronous call is already in progress. 

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

4b9b3361

Ответ 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--;
        }