.NET 发送电子邮件的最佳方法(System.Net.Mail 有问题)
Posted
技术标签:
【中文标题】.NET 发送电子邮件的最佳方法(System.Net.Mail 有问题)【英文标题】:.NET Best Method to Send Email (System.Net.Mail has issues) 【发布时间】:2010-10-30 03:16:51 【问题描述】:这似乎很简单。我需要从一些 ASP.NET 应用程序发送电子邮件。我需要始终如一地做到这一点,而不会出现奇怪的错误,也不会出现 CPU 利用率过高的情况。我不是在谈论群发电子邮件,只是偶尔发送电子邮件。
System.Net.Mail 似乎被严重破坏了。 SmtpClient 不发出 Quit 命令(可能是因为 Microsoft(R) 对以下规范不感兴趣),因此连接保持打开状态。因此,如果有人尝试在该连接最终关闭之前发送电子邮件,您可能会从 SMTP 服务器收到关于打开的连接过多的错误。这是 Microsoft(R) 对修复完全不感兴趣的错误。见这里:
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=146711
另外,如果您环顾四周,建议使用此代码来解决此问题:
smtpClient.ServicePoint.MaxIdleTime = 1;
smtpClient.ServicePoint.ConnectionLimit = 1;
好的,是的,这确实“解决”了连接保持打开状态的问题。但是,如果您愿意,这始终如一地在服务器上尝试,这会导致运行进程(在本例中为 w3wp.exe)的 CPU 跳跃并保持在 100%,直到您的应用程序池被回收。无论出于何种原因,运行 mscorwks.dll!CreateApplicationContext 的线程都是罪魁祸首。
这有一个非常好的副作用,如果您在一个对持续 100% CPU 使用不满意的 Web 主机上运行,您将禁用您的应用程序池。因此,这并不像某些人建议的那样微不足道。
所以我的问题是怎么办?我需要做的就是这么简单;然而,让那些“打开的连接太多”错误是不可接受的,100% 的 CPU 使用率也是不可接受的。我不想购买第三方组件,不是因为我便宜,而是我购买了足够多的组件和 MSDN 订阅,以至于不得不为简单的 SMTP 功能支付 100 至 300 美元似乎很疯狂。
我读到将 MaxIdleTime 设置得更高会有所帮助,但我对此表示怀疑。我不想因为 Microsoft 不想遵循 SMTP 规范而冒险禁用我的应用程序池。
编辑:我查看了 quiksoft.com 的组件,但它不支持 SMTP 身份验证,而且价格为 500 美元。一定有办法解决这个问题。
【问题讨论】:
根据您提供的链接,该错误似乎已得到修复。您是否可能缺少服务包? 我看到上面写着“已解决”。但是我没有看到任何其他注释。我已将服务器更新到最新的 SP 以及所有这些。当我以前看到它时,通常似乎 MS 只是意味着他们不会修复它。他们还列出了我提到的“解决方法”。可能会更清楚。 【参考方案1】:在 .NET 4.0 中,SmtpClient 现在是一次性的。 SMTP QUIT 命令在处置时发出,例如在 using 块中使用时。
【讨论】:
这个是正确答案,我自己测试过。请为此评论 +1。 这是正确的答案,你可以在 using 块中使用 SmtpClient。 我试过这个,但它似乎不像宣传的那样工作。我仍然收到“#4.x.2 Too many messages for this session”错误。其他人有同样的问题吗? 将 SmtpClient 的处置(正确)与亚马逊的 AWS SES 结合使用,为我解决了大量频繁的发送错误。就我而言,代码最初是在 .NET 2.0 下构建的,升级到 4.x,然后迁移到 AWS。 SMTP 主机被换成了 SES,并且由于 smtp 会话保持打开状态,它抛出了各种错误。一个简单的 using 块解决了它。 这个问题在 .NET Core 库中被重新引入,即使文档错误地声明它将发送 QUIT 命令。有关详细信息,请参阅github.com/dotnet/corefx/issues/34868。【参考方案2】:我们使用hMailserver 取得了巨大成功。该配置可能需要一段时间才能习惯,但它是一款很棒的免费邮件服务器产品。
如果您想推出自己的产品(几年前我在 CDONTS 玩得很开心时就这样做了),您可以从下面的代码开始,并根据自己的喜好进行定制。它使用 TcpClient 直接创建到邮件服务器的 TCP 连接。当有这么多已建立和调试的解决方案时,我不建议这样做,但我发现这对于调试和确定预制 MS 邮件组件的问题非常有用。
private void Send_Email()
#region Email Sending Function
string strMail = "";
try
// See RFC821 http://www.faqs.org/rfcs/rfc821.html for more specs
// TcpClient is an abstraction of a TCP Socket connection
TcpClient myTCP = new TcpClient();
// Connect to the mail server's port 25
myTCP.Connect(mailserver, 25);
// Open a network stream which sends data to/from the TcpClient's socket
System.Net.Sockets.NetworkStream ns = myTCP.GetStream();
// The data to send to the mail server, basically a raw SMTP mail message
strMail = "HELO\n";
strMail += "MAIL FROM:from@address.com\n";
strMail += "RCPT TO:" + recipient + "\n";
strMail += "DATA\n";
strMail += "Subject: mySubject\n";
strMail += "To:" + recipient + "\n";
strMail += "From: \"From Real Name\" <from@address.com>\n";
strMail += "\n";
strMail += " ---------------------------------------\n";
strMail += "Name: " + txtName.Text + "\n";
strMail += "Address1: " + txtAddress1.Text + "\n";
strMail += "Address2: " + txtAddress2.Text + "\n";
strMail += "City: " + txtCity.Text + "\n";
strMail += "State: " + txtState.Text + "\n";
strMail += "Zip: " + txtZip.Text + "\n";
strMail += "Email: " + txtEmail.Text + "\n";
strMail += "Dealer: " + txtDealer.Text + "\n";
strMail += " ---------------------------------------\n";
strMail += "THIS IS AN AUTOMATED EMAIL SYSTEM. DO NOT REPLY TO THIS ADDRESS.\n";
strMail += "\n.\n";
// Defines encoding of string into Bytes (network stream needs
// an array of bytes to send -- can't send strings)
ASCIIEncoding AE = new ASCIIEncoding();
byte[] ByteArray = AE.GetBytes(strMail);
// send the byte-encoded string to the networkstream -> mail server:25
ns.Write(ByteArray, 0, ByteArray.Length);
//ns.Read(ByteArray, 0, ByteArray.Length);
//lblStatus.Text = ByteArray.ToString();
// close the network stream
ns.Close();
// close the TCP connection
myTCP.Close();
catch(Exception ex)
throw new Exception("Couldn't send email: <p>" + ex.Message);
#endregion
【讨论】:
【参考方案3】:我遇到了与上述设置相同的 CPU 利用率问题。我最终向 Microsoft 开了一张票,以确定问题的原因。 CPU 利用率问题在于 ServicePoint 类。在 ServicePoint 类内部,有一个每 (MaxIdleTime/2) 毫秒运行一次的计时器。看到问题了吗?通过将 MaxIdleTime 值更改为 2,CPU 利用率将下降到正常水平。
【讨论】:
我不太“看到问题”,但是这个修复工作。有趣的是,默认的 MaxIdleTime 为 100000。当我们将其设置为 1 时,我们将 CPU 飙升至接近 100%。就像您报告的那样,将 MaxIdleTime 设置为 2 会使 CPU 降至正常水平。 @Scott Ferguson:问题在于计时器将以 (1/2) 毫秒运行,在整数除法中将计算为……每 0 毫秒。哎呀。【参考方案4】:我使用 Sproc 发送大部分邮件。我什至可以附加文件。
创建过程 [dbo].[sendMail_With_CDOMessage] @to VARCHAR(64), @CC VARCHAR(1024)='', @BCC VARCHAR(1024)='', @subject VARCHAR(500)='', @body VARCHAR(8000)='' , @来自 VARCHAR(64), @filename VARCHAR(255)='', @priority INT = 0 作为 开始 设置无计数 宣布 @handle INT, @return 整数, @s VARCHAR(64), @sc VARCHAR(1024), @up CHAR(27), @server VARCHAR(255) SET @s = '"http://schemas.microsoft.com/cdo/configuration/' 选择 @s = 'Configuration.Fields(' + @s, @up = 'Configuration.Fields.Update', @server = 'smtp.yourdomain.com' EXEC @return = sp_OACreate 'CDO.Message', @handle OUT SET @sc = @s + 'sendusing").Value' EXEC @return = sp_OASetProperty @handle, @sc, '2' SET @sc = @s + 'smtpserver").Value' 执行@return = sp_OASetProperty @handle、@sc、@server EXEC @return = sp_OAMethod @handle, @up, NULL EXEC @return = sp_OASetProperty @handle, 'To', @to EXEC @return = sp_OASetProperty @handle, 'CC', @CC EXEC @return = sp_OASetProperty @handle, 'BCC', @BCC EXEC @return = sp_OASetProperty @handle, 'From', @from EXEC @return = sp_OASetProperty @handle, 'Subject', @subject EXEC @return = sp_OASetProperty @handle, 'HTMLBody', @body EXEC @return = sp_OASetProperty @handle, 'Priority', 'cdoHigh' 如果@filename 不为空 EXEC @return = sp_OAMethod @handle, 'AddAttachment', NULL, @filename EXEC @return = sp_OAMethod @handle, '发送', NULL 如果 @return 0 开始 PRINT '邮件失败。' 如果 @from 为空 PRINT '来自未定义的地址。 别的 ' PRINT '检查服务器是否有效。 结尾 别的 PRINT '邮件已发送。 执行@return = sp_OADestroy @handle 结尾【讨论】:
【参考方案5】:我过去使用过 Quicksoft,没有任何抱怨。您可以尝试的另一件事是将 SMTP 配置切换为使用拾取文件夹而不是使用网络发送,这应该可以解决“它不发送 QUIT”问题。
【讨论】:
【参考方案6】:我一直使用 Quiksoft 的 EasyMail .NET 组件,完全没有问题。
产品首页:http://www.quiksoft.com/emdotnet/
如果您只需要发送电子邮件,他们也有该组件的免费版本:
http://www.quiksoft.com/freesmtp/
【讨论】:
我同意。在 System.Net.Mail 出现许多问题后,我们切换到 Quiksoft,并且从未回头。 这很有趣,我会看看这个。如果其他人有其他建议,我也将不胜感激! 免费版不支持使用用户名和密码的 SMTP 身份验证。这要花 500 美元。 Quiksoft 不见了。【参考方案7】:虽然到目前为止我在 System.Net.Mail
方面没有遇到任何具体问题,但您始终可以使用旧的 System.Web.Mail
API,它是 CDOSYS 的包装器。
【讨论】:
就 System.Net.Mail 没有问题而言,您是否尝试过从您的一个页面发送一封电子邮件,然后立即从该页面发送另一封邮件?您的服务器可能允许多个连接。我敢打赌,如果您运行 TCPView,您的连接会长时间保持打开状态。另外,System.Web.Mail 不依赖于本地 SMTP 服务吗?我需要通过已正确配置 MX 等的远程主机发送电子邮件,以避免被标记为垃圾邮件。 我不否认它有问题。我只是说我没有遇到它。我用的不多(只是简单的“联系我们”表格)。 System.Web.Mail 不依赖本地 SMTP 服务器,支持使用远程 SMTP 服务器。我已经广泛使用了。它不是很优雅,但一直运行良好。 哦,我不是在争论,只是好奇。 :) 我也不是。只是想确保我没有认可它。以上是关于.NET 发送电子邮件的最佳方法(System.Net.Mail 有问题)的主要内容,如果未能解决你的问题,请参考以下文章
将 ASP.Net Core Web App 项目发送给某人的最佳实践?
(Win/C#/.Net) Applet 需要每隔一小时轮询一次——关于最佳方法的建议?
使用 rake 任务时检查电子邮件是不是发送一次的最佳方法是啥?