18

这似乎很简单。我需要从一些 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 设置得更高会有所帮助,但我对此表示怀疑。我不想因为微软不想遵循 SMTP 规范而冒险禁用我的应用程序池。

编辑: 我查看了 quiksoft.com 组件,但它不支持 SMTP 身份验证,而且价格为 500 美元。一定有办法解决这个问题。

4

7 回答 7

30

在 .NET 4.0 中,SmtpClient 现在是一次性的。SMTP QUIT 命令在处置时发出,例如在使用块中使用时。

于 2010-11-29T18:40:42.733 回答
22

我在所描述的设置中遇到了同样的 CPU 利用率问题。我最终向 Microsoft 开了一张票,以确定问题的原因。CPU 利用率问题在于 ServicePoint 类。在 ServicePoint 类内部,有一个每 (MaxIdleTime/2) 毫秒运行一次的计时器。看到问题了吗?通过将 MaxIdleTime 值更改为 2,CPU 利用率将下降到正常水平。

于 2009-07-27T21:26:06.647 回答
2

我一直使用 Quiksoft 的 EasyMail .NET 组件,完全没有问题。

产品主页:http ://www.quiksoft.com/emdotnet/

如果您只需要发送电子邮件,他们也有该组件的免费版本:

http://www.quiksoft.com/freesmtp/

于 2009-05-30T18:13:19.753 回答
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

    }
于 2010-11-04T06:25:42.330 回答
1

虽然到目前为止我没有遇到任何具体问题System.Net.Mail,但您始终可以使用旧System.Web.MailAPI,它是 CDOSYS 的包装器。

于 2009-05-30T18:12:16.450 回答
1

我过去使用过 Quicksoft,没有任何抱怨。您可以尝试的另一件事是将 SMTP 配置切换为使用拾取文件夹而不是使用网络发送,这应该可以解决“它不发送 QUIT”问题。

于 2009-05-31T13:04:02.207 回答
-1

我使用 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  
结尾  

于 2009-06-01T18:41:52.533 回答