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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/389709/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 01:37:46  来源:igfitidea点击:

Why can SmtpClient.SendAsync only be called once?

c#smtpclientsystem.net.mail

提问by Daniel

I'm trying to write a notification service (for completely legit non-spam purposes) in .NET using SmtpClient. Initially I just looped through each message and sent it, however this is slow and I would like to improve the speed. So, I switched to using 'SendAsync', but now get the following error on the second call:

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

An asynchronous call is already in progress. 

I read this to mean that MS crippled System.Net.Mail to prevent mass-mailers. Is this correct? If so, is there a better way to do this in .NET, and still be able to log the results of each email(which is important to our client). If not, why can SendAsync only be called once?

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

采纳答案by Darin Dimitrov

According to the documentation:

根据文档

After calling SendAsync, you must wait for the e-mail transmission to complete before attempting to send another e-mail message using Send or SendAsync.

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

So to send multiple mails at the same time you need multiple SmtpClient instances.

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

回答by Tamas Czinege

Obviously, this is not an attempt to stop mass mailers.

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

The reason is that the SmtpClient class is not thread safe. If you want to send multiple emails simultaneously, you have to spawn a few worker threads (there are a few ways to do that in the .NET Framework) and create separate instances of SmtpClient in each of them.

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

回答by Colby Africa

You may only send one at a time per SMTP client. If you wish to make more than one send call, create more than one SMTP client.

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

HTH,

哈,

Colby Africa

科尔比非洲

回答by Gant

I think you misunderstand the XXXAsyncclass of methods. The purpose of these asynchronous calls is to allow the program to continue running, without the need of the method to finish processing and return first. You can then proceed with the result later by subscribe to something like XXXReceivedevent of the object.

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

To send more than one mail simultaneously, you may consider using more Threads.

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

回答by Lasse V. Karlsen

As noticed by everyone else here, you can only send one email at a time, but the way to send another once the first has been sent is to handle the .SendCompleted event of the SmtpClient class, and then move on to the next email and send that.

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

If you want to send many emails simultaneously, then as the others have said, use multiple SmtpClient objects.

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

回答by kntcnrg

You might be able to use the following:

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

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

This should allow your messages to be queued and sent as threads become available.

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

回答by Billy Willoughby

There is a reason to reuse the SmtpClient, it limits the # of connections to the SMTP server. I cannot instantiate a new class SmtpClientclass for each thread the reports are building on or the SMTP server will balk with too many connections error. This is the solution I came up with when I couldn't find an answer here.

重用SmtpClient是有原因的,它限制了与 SMTP 服务器的连接数。我无法为构建报告的每个线程实例化一个新类SmtpClient类,否则 SMTP 服务器将因连接错误过多而犹豫不决。这是我在这里找不到答案时想到的解决方案。

I ended up using an AutoResetEventfor keeping everything in sync. That way, I can keep calling my SendAsyncin each thread, but wait for it to process the email and use the SendCompleteevent to reset it so the next one can continue.

我最终使用AutoResetEvent来保持所有内容同步。这样,我可以在每个线程中继续调用我的SendAsync,但等待它处理电子邮件并使用SendComplete事件重置它,以便下一个可以继续。

I setup the Auto Reset Event.

我设置了自动重置事件。

        AutoResetEvent _autoResetEvent = new AutoResetEvent(true);

I setup the shared SMTP Client when my class is instantiated.

当我的类被实例化时,我设置了共享的 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;

Then when I call the send async, I wait for the event to clear, then send the next one.

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

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

I use the SMTPClient SendComplete event to reset the AutoResetEvent so the next email will send.

我使用 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--;
        }