为什么只能调用一次SmtpClient.SendAsync?

时间:2008-12-23 18:38:00

标签: c# smtpclient system.net.mail

我正在尝试使用SmtpClient在.NET中编写通知服务(用于完全合法的非垃圾邮件目的)。最初我只是通过每个消息循环并发送它,但这很慢,我想提高速度。所以,我切换到使用'SendAsync',但现在第二次调用时出现以下错误:

An asynchronous call is already in progress. 

我读到这意味着MS瘫痪了System.Net.Mail以防止群发邮件。它是否正确?如果是这样,有没有更好的方法在.NET中执行此操作,并且仍然能够记录每封电子邮件的结果(这对我们的客户来说很重要)。如果没有,为什么只能调用一次SendAsync?

7 个答案:

答案 0 :(得分:35)

根据documentation

  

调用SendAsync后,您必须等待   用于电子邮件传输   在尝试发送之前完成   另一封使用Send或发送电子邮件的邮件   SendAsync。

因此,要同时发送多封邮件,您需要多个SmtpClient实例。

答案 1 :(得分:6)

您可以使用以下内容:

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

这应该允许您的消息排队并在线程可用时发送。

答案 2 :(得分:4)

显然,这不是阻止群发邮件的尝试。

原因是SmtpClient类不是线程安全的。如果要同时发送多个电子邮件,则必须生成一些工作线程(在.NET Framework中有几种方法可以执行此操作)并在每个线程中创建单独的SmtpClient实例。

答案 3 :(得分:4)

我认为你误解了XXXAsync类方法。这些异步调用的目的是允许程序继续运行,而不需要方法来完成处理并首先返回。然后,您可以通过订阅对象的XXXReceived事件来继续处理结果。

要同时发送多封邮件,您可以考虑使用更多Thread s。

答案 4 :(得分:2)

每个SMTP客户端一次只能发送一个。如果您希望进行多个发送呼叫,请创建多个SMTP客户端。

HTH,

非洲科尔比

答案 5 :(得分:2)

正如此处其他所有人所注意到的那样,您一次只能发送一封电子邮件,但是在发送第一封电子邮件后发送另一封电子邮件的方式是处理SmtpClient类的.SendCompleted事件,然后转到下一封电子邮件并发送。

如果您想同时发送许多电子邮件,那么就像其他人所说的那样,使用多个SmtpClient对象。

答案 6 :(得分:1)

有理由重复使用 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--;
        }