22

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

An asynchronous call is already in progress. 

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

4

7 回答 7

35

根据文档

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

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

于 2008-12-23T18:43:15.683 回答
6

您也许可以使用以下内容:

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

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

于 2009-09-01T19:54:51.953 回答
4

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

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

于 2008-12-23T18:42:27.853 回答
4

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

要同时发送多封邮件,您可以考虑使用 more Threads。

于 2008-12-23T18:46:31.387 回答
2

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

高温下,

科尔比非洲

于 2008-12-23T18:43:39.130 回答
2

正如这里的其他人所注意到的,您一次只能发送一封电子邮件,但是在发送第一封电子邮件后发送另一封电子邮件的方法是处理 SmtpClient 类的 .SendCompleted 事件,然后继续处理下一封电子邮件并发送那个。

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

于 2008-12-23T18:52:16.757 回答
2

重用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;

然后当我调用异步发送时,我等待事件清除,然后发送下一个。

        _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--;
        }
于 2018-06-12T14:46:59.810 回答