Лучший способ отправки электронной почты .NET (проблемы с System.Net.Mail) - PullRequest
18 голосов
/ 30 мая 2009

Это кажется довольно простым. Мне нужно отправить электронную почту из некоторых приложений ASP.NET. Я должен делать это последовательно без странных ошибок и без использования процессора, идущего вверх. Я не говорю о массовых рассылках, просто случайные письма.

System.Net.Mail кажется ужасно сломанным. SmtpClient не выдает команду Quit (это может быть потому, что Microsoft (R) не заинтересована в следующих спецификациях), поэтому соединение остается открытым. Поэтому, если кто-то попытается отправить электронное письмо до того, как это соединение finally закроется, вы можете получить ошибки от SMTP-сервера об слишком большом количестве открытых соединений. Это ошибка, которую Microsoft (R) совершенно не заинтересована в исправлении. Смотрите здесь:

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=146711

Также, если вы посмотрите вокруг, некоторые предложат использовать этот код для решения этой проблемы:

smtpClient.ServicePoint.MaxIdleTime = 1;
smtpClient.ServicePoint.ConnectionLimit = 1;

Хорошо, да, это "решает" проблему, когда соединения остаются открытыми. Тем не менее, это последовательно, попробуйте на сервере, если хотите, заставляет процессор, на котором запущен процесс (в данном случае w3wp.exe), переходить на 100% до тех пор, пока ваш пул приложений не будет перезагружен. По какой-то причине виновником является поток, который запускает mscorwks.dll! CreateApplicationContext.

Это имеет очень приятный побочный эффект: если вы работаете на веб-хосте, который хмурится при постоянной загрузке процессора на 100%, вы отключите пул приложений. Так что это не так тривиально, как некоторые предполагают.

Итак, мой вопрос: что делать? Что мне нужно сделать, это так просто; однако получение ошибок «слишком много открытых соединений» неприемлемо и 100% загрузка ЦП. Я не хочу покупать сторонние компоненты не потому, что я дешевый, но я покупаю достаточно компонентов и подписку MSDN, так что кажется просто сумасшедшим, чтобы выложить 100-300 долларов за простую функциональность SMTP.

Я читал, что установка MaxIdleTime выше может помочь, но я скептически отношусь к этому. Я не хочу рисковать отключением пула приложений только потому, что Microsoft не хочет следовать спецификации SMTP.

Редактировать: Я посмотрел на компоненты quiksoft.com, однако он не поддерживает аутентификацию SMTP и стоит 500 долларов. Должно быть решение этой проблемы.

Ответы [ 7 ]

29 голосов
/ 29 ноября 2010

В .NET 4.0 SmtpClient теперь одноразовый. Команда SMTP QUIT выдается при утилизации, например, при использовании в блоке использования.

22 голосов
/ 28 июля 2009

Я столкнулся с той же проблемой загрузки процессора с описанными настройками. Я закончил тем, что открыл билет с Microsoft, чтобы определить причину проблемы. Проблема использования ЦП лежит в классе ServicePoint. Внутри класса ServicePoint есть таймер, который запускается каждые (MaxIdleTime / 2) миллисекунды. Видишь проблему? При изменении значения MaxIdleTime на 2 загрузка ЦП снизится до нормального уровня.

2 голосов
/ 04 ноября 2010

Мы использовали 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

    }
2 голосов
/ 30 мая 2009

Я всегда использовал компоненты EasyMail .NET Quiksoft без проблем.

Домашняя страница продукта: http://www.quiksoft.com/emdotnet/

У них также есть бесплатная версия компонента, если вам нужно только отправить электронную почту:

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

1 голос
/ 31 мая 2009

Я использовал Quicksoft в прошлом, и у меня нет жалоб. Еще одна вещь, которую вы можете попробовать, это переключить конфигурацию SMTP на использование папки раскладки вместо отправки по сети, что должно решить проблему «она не отправляет»

1 голос
/ 30 мая 2009

Пока у меня не было особых проблем с System.Net.Mail, вы всегда можете использовать более старый System.Web.Mail API, который является оболочкой для CDOSYS.

0 голосов
/ 01 июня 2009

Я отправляю большую часть своей почты, используя Sproc. Я даже могу прикрепить файл.


CREATE PROCEDURE [dbo].[sendMail_With_CDOMessage]  
    @to VARCHAR(64), 
    @CC VARCHAR(1024)='',
    @BCC VARCHAR(1024)='',
    @subject VARCHAR(500)='', 
    @body VARCHAR(8000)='' ,
    @from VARCHAR(64),
    @filename VARCHAR(255)='',
    @priority INT = 0
AS  
BEGIN  
    SET NOCOUNT ON  

    DECLARE  
        @handle INT,  
        @return INT,  
        @s VARCHAR(64),  
        @sc VARCHAR(1024),  
        @up CHAR(27),   
        @server VARCHAR(255)  

    SET @s = '"http://schemas.microsoft.com/cdo/configuration/'  

    SELECT  
        @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'  
    EXEC @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'  

    IF @filename IS NOT NULL  
        EXEC @return = sp_OAMethod @handle, 'AddAttachment', NULL, @filename  

    EXEC @return = sp_OAMethod @handle, 'Send', NULL  
    IF @return  0  
    BEGIN  
        PRINT 'Mail failed.'  
        IF @from IS NULL  
            PRINT 'From address undefined.'  
        ELSE  
            PRINT 'Check that server is valid.' 
    END 
    ELSE  
        PRINT 'Mail sent.'  

    EXEC @return = sp_OADestroy @handle  
END  

...