使用SSL时,为什么SmtpClient会因多个不同的异常而失败?

时间:2016-02-10 15:08:48

标签: c# ssl smtp system.net.mail system.net

我尝试使用启用了SSL的System.Net.Mail.SmtpClient发送邮件。虽然不改变任何外部因素(即命中F5,然后再次点击F5 - 或者甚至在循环中),它可以工作一段时间,但大部分时间都会失败。

示例代码:

public void SendMail()
        {
            using (var client = new SmtpClient())
            {
                MailMessage mailMessage = new MailMessage();
                client.EnableSsl = true;
                client.Host = "smtp.example.com";
                client.Timeout = 10000;
                client.DeliveryMethod = SmtpDeliveryMethod.Network;
                mailMessage.From = new MailAddress("from@example.com");
                mailMessage.To.Add(new MailAddress("to@example.com"));
                mailMessage.Subject = "Test";
                mailMessage.Body = "Message " + DateTime.Now.ToString();
                try
                {
                    client.Send(mailMessage);
                }
                catch (Exception ex)
                {
                    // This being a Pokemon block is besides the point
                }
            }
        }

在我的catch块中,我只是暂停,但如果我为System.NetSystem.Net.Sockets设置了跟踪,我可以看到许多不同的例外:

  
      
  • 无法从传输连接读取数据:阻止操作因调用WSACancelBlockingCall而中断
  •   
  • 无法从传输连接中读取数据:已建立的连接已被主机中的软件中止
  •   
  • 无法访问已处置的对象
  •   
  • 此流不支持阅读
  •   
  • 此流不支持写入
  •   

所有这些都发生在EHLO,STARTTLS和身份验证之后。当抛出异常时,客户端和服务器以加密方式进行聊天。

System.Net.Mail.SmtpClient.Send()是所有调用堆栈的唯一共同框架。

我当然尝试过多种方式更改代码,例如设置client.UseDefaultCredentials,使用client.SendAsync()等等。但我怀疑在不同的时间敲响连接是完全不同的 - 可能涉及其中一台机器的配置错误?

所以我已经检查了事件日志中有关连接混乱的迹象,但我还没找到任何东西。

有关我应该在这里寻找什么的任何建议吗?

3 个答案:

答案 0 :(得分:2)

有时超时只是暂停......

进一步深入研究这个问题,我使用Wireshark来了解网络层面的情况。

当发生故障时,客户端计算机在EHLO之后大约10秒内总是发送TCP [RST,ACK]数据包。一段时间显着接近我的超时值。当我将超时值加倍时,邮件每次都没有失败。

当然,如果我们花费超过10秒钟发送每封邮件,我们很快就会在我们的生产系统中将邮件排队几个小时,所以我需要找出为什么花了这么长时间。

一个细节引起了我的注意:Wireshark trace

客户端在两个数据包之间花了超过四秒钟。它通常在数据包之间花费数百毫秒,而服务器将在几十年内响应。

下一步是构建没有调试符号的代码,并在没有附加调试器的情况下再次测量。每封电子邮件立即发送的时间不到一秒钟。现在,对于我而言,附加调试器的代码运行速度较慢并不是新闻,但看到它运行速度慢得令人惊讶。

所以我要吸取的教训是:尽量不要过度分析,并且在涉及计时时偶尔会杀掉调试器。

答案 1 :(得分:1)

这应该是一个评论(我没有评论的声誉),但它可能是一个防火墙问题,因为你得到一个“无法从传输连接读取数据:已建立的连接被中止主机中的软件“错误(基本上,防火墙或防病毒取消了您发送邮件的尝试)。

错误“无法从传输连接中读取数据:阻止操作被WSACancelBlockingCall调用中断”会在流关闭时发生 - 请参阅this question - 并且您列出的其他错误看起来像它们与在关闭时尝试访问流有关。

所有这些错误都指向一个常见的原因,一个连接在您和服务器之间的某处被取消,很可能在您的机器/网络上,并且您会得到不同的错误,具体取决于效果传播到您的应用程序的位置。

另一件事是,如果你使用

Credentials = new NetworkCredential(fromAddress.Address, fromPassword)

然后你必须在之后启用ssl,而不是之前,否则它已被清除。

答案 2 :(得分:-1)

我想您错过了设置主机的Credentials属性。但是,异常的另一个原因可能是主机名和端口错误。尝试类似:

    public void SendMail()
    {
        try
        {
            var fromAddress = new MailAddress("from@example.com", "Some text");
            var toAddress = new MailAddress("to@example.com");
            const string fromPassword = "from account password";

            var smtp = new SmtpClient
            {
                Host = "smtp.example.com",
                Port = "your host port", //for gmail is 587
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
            };
            using (var message = new MailMessage(fromAddress, toAddress)
            {
                Subject = "Test",
                Body = "Message " + DateTime.Now.ToString()
            })
            {
                smtp.Send(message);
            }
        }
        catch (Exception ex)
        {
            //do something
        }
   }

使用gmail主机测试smtp.gmail.com并且它可以正常工作