22 Stimmen

Warum kann SmtpClient.SendAsync nur einmal aufgerufen werden?

Ich versuche, einen Benachrichtigungsdienst (für völlig legit nicht-Spam Zwecke) in .NET mit SmtpClient zu schreiben. Ursprünglich habe ich nur in einer Schleife durch jede Nachricht gesendet, aber das ist langsam und ich möchte die Geschwindigkeit zu verbessern. Also bin ich zu 'SendAsync' übergegangen, aber jetzt bekomme ich beim zweiten Aufruf den folgenden Fehler:

An asynchronous call is already in progress. 

Ich habe das so verstanden, dass MS System.Net.Mail verkrüppelt hat, um Massenmailer zu verhindern. Ist das richtig? Wenn ja, gibt es eine bessere Möglichkeit, dies in .NET zu tun und trotzdem in der Lage zu sein, die Ergebnisse jeder E-Mail zu protokollieren (was für unseren Kunden wichtig ist). Wenn nicht, warum kann SendAsync nur einmal aufgerufen werden?

2voto

Lasse V. Karlsen Punkte 364542

Wie von allen anderen hier bemerkt, können Sie nur eine E-Mail auf einmal senden, aber der Weg, um eine weitere zu senden, sobald die erste gesendet wurde, ist das Ereignis .SendCompleted der SmtpClient-Klasse zu behandeln, und dann auf die nächste E-Mail und senden, dass.

Wenn Sie viele E-Mails gleichzeitig versenden wollen, dann verwenden Sie, wie bereits erwähnt, mehrere SmtpClient-Objekte.

2voto

Billy Willoughby Punkte 737

Es gibt einen Grund für die Wiederverwendung der SmtpClient wird die Anzahl der Verbindungen zum SMTP-Server begrenzt. Ich kann eine neue Klasse nicht instanziieren SmtpClient Klasse für jeden Thread, auf dem die Berichte erstellt werden, oder der SMTP-Server wird mit dem Fehler "zu viele Verbindungen" abgewiesen. Dies ist die Lösung, die ich gefunden habe, als ich hier keine Antwort finden konnte.

Am Ende habe ich eine AutoResetEvent um alles auf dem Laufenden zu halten. Auf diese Weise kann ich weiterhin meine SendAsync in jedem Thread, sondern warten, bis er die E-Mail verarbeitet hat, und verwenden die SendComplete um es zurückzusetzen, damit das nächste Ereignis fortgesetzt werden kann.

Ich habe das Auto-Reset-Ereignis eingerichtet.

        AutoResetEvent _autoResetEvent = new AutoResetEvent(true);

Ich richte den gemeinsamen SMTP-Client ein, wenn meine Klasse instanziiert wird.

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

Wenn ich dann das asynchrone Senden aufrufe, warte ich darauf, dass das Ereignis gelöscht wird, und sende dann das nächste Ereignis.

        _autoResetEvent.WaitOne();
        _smtpServer.SendAsync(mail, mail);
        mailWaiting++;

Ich verwende das SMTPClient SendComplete-Ereignis, um das AutoResetEvent zurückzusetzen, damit die nächste E-Mail gesendet wird.

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

1 Stimmen

Wenn Sie mit .NET 4.0 oder höher arbeiten, sollten Sie einen Blick auf SemaphoreSlim o ManualResetEventSlim die schneller sind, da sie sich nicht auf die Win32-Kernelobjekte ( docs.microsoft.com/de-us/dotnet/standard/threading/ ).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X