为什么在使用 SSL 时 SmtpClient 会失败并出现几个不同的异常?
Why does SmtpClient fail with several different Exceptions when using SSL?
我正在尝试使用启用了 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.Net
和 System.Net.Sockets
设置跟踪,我可以看到许多不同的异常:
- Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall
- Unable to read data from the transport connection: An established connection was aborted by the software in your host machine
- Cannot access a disposed object
- Reading is not supported on this stream
- Writing is not supported on this stream
所有这些都会发生 post EHLO、STARTTLS 和身份验证。抛出异常时,客户端和服务器正在以加密方式聊天。
System.Net.Mail.SmtpClient.Send()
是所有调用堆栈唯一共有的框架。
当然,我尝试过以多种方式更改代码,例如设置 client.UseDefaultCredentials
、使用 client.SendAsync()
等等。但我怀疑在不同时间断开连接是完全不同的事情——可能是其中一台机器的配置错误?
因此,我检查了事件日志以查找连接出现问题的迹象,但我没有发现任何问题。
关于我应该在这里寻找什么的任何建议?
我想你没有设置主机的 Credentials
属性。然而,另一个异常原因可能是错误的主机名和 port.Try 之类的东西:
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
}
}
它是用 smtp.gmail.com
的 gmail 主机测试的,它工作正常
这应该是一条评论(我没有发表评论的名誉),但它可能是防火墙问题,因为您收到 "Unable to read data from the transport connection: An established connection was aborted by the software in your host machine" 错误(基本上是防火墙或防病毒软件取消了您发送邮件的尝试)。
错误 "Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall" 在流关闭时发生 - 请参阅 this question - 你列出的其他错误似乎与在流关闭时尝试访问流有关,也是。
所有这些错误都指向一个共同的原因,您和服务器之间的某处连接被取消,很可能是在您的机器/网络上,并且您会收到不同的错误,具体取决于影响传播到您的应用程序的时间点。
另一件事是,如果你使用
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
那么你必须在之后启用ssl,而不是之前,否则它被清除了。
有时超时只是超时...
为了进一步研究这个问题,我使用 Wireshark 查看网络级别发生了什么。
当故障发生时,客户端机器总是在EHLO 后10 秒左右发送一个TCP [RST, ACK] 数据包。时间跨度明显接近我的超时值。当我将超时值加倍时,邮件每次都顺利通过。
当然,如果我们发送每封邮件的时间超过 10 秒,我们的生产系统很快就会让邮件排队数小时,所以我需要找出为什么要花这么长时间。
一个细节引起了我的注意:
客户端在两个数据包之间花费了超过四秒的时间。而且它通常在数据包之间花费数百毫秒,而服务器会在几十毫秒内响应。
下一步是在没有调试符号的情况下构建代码,并在没有附加调试器的情况下再次测量。立即发送每封电子邮件所花费的时间是亚秒级的。现在对我来说,附加调试器后代码 运行 变慢已经不是什么新闻了,但看到它 运行 慢得多令人惊讶。
所以我学到的教训是:尽量不要过度分析,当涉及到时间时,偶尔关闭调试器。
我正在尝试使用启用了 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.Net
和 System.Net.Sockets
设置跟踪,我可以看到许多不同的异常:
- Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall
- Unable to read data from the transport connection: An established connection was aborted by the software in your host machine
- Cannot access a disposed object
- Reading is not supported on this stream
- Writing is not supported on this stream
所有这些都会发生 post EHLO、STARTTLS 和身份验证。抛出异常时,客户端和服务器正在以加密方式聊天。
System.Net.Mail.SmtpClient.Send()
是所有调用堆栈唯一共有的框架。
当然,我尝试过以多种方式更改代码,例如设置 client.UseDefaultCredentials
、使用 client.SendAsync()
等等。但我怀疑在不同时间断开连接是完全不同的事情——可能是其中一台机器的配置错误?
因此,我检查了事件日志以查找连接出现问题的迹象,但我没有发现任何问题。
关于我应该在这里寻找什么的任何建议?
我想你没有设置主机的 Credentials
属性。然而,另一个异常原因可能是错误的主机名和 port.Try 之类的东西:
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
}
}
它是用 smtp.gmail.com
的 gmail 主机测试的,它工作正常
这应该是一条评论(我没有发表评论的名誉),但它可能是防火墙问题,因为您收到 "Unable to read data from the transport connection: An established connection was aborted by the software in your host machine" 错误(基本上是防火墙或防病毒软件取消了您发送邮件的尝试)。
错误 "Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall" 在流关闭时发生 - 请参阅 this question - 你列出的其他错误似乎与在流关闭时尝试访问流有关,也是。
所有这些错误都指向一个共同的原因,您和服务器之间的某处连接被取消,很可能是在您的机器/网络上,并且您会收到不同的错误,具体取决于影响传播到您的应用程序的时间点。
另一件事是,如果你使用
Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
那么你必须在之后启用ssl,而不是之前,否则它被清除了。
有时超时只是超时...
为了进一步研究这个问题,我使用 Wireshark 查看网络级别发生了什么。
当故障发生时,客户端机器总是在EHLO 后10 秒左右发送一个TCP [RST, ACK] 数据包。时间跨度明显接近我的超时值。当我将超时值加倍时,邮件每次都顺利通过。
当然,如果我们发送每封邮件的时间超过 10 秒,我们的生产系统很快就会让邮件排队数小时,所以我需要找出为什么要花这么长时间。
一个细节引起了我的注意:
客户端在两个数据包之间花费了超过四秒的时间。而且它通常在数据包之间花费数百毫秒,而服务器会在几十毫秒内响应。
下一步是在没有调试符号的情况下构建代码,并在没有附加调试器的情况下再次测量。立即发送每封电子邮件所花费的时间是亚秒级的。现在对我来说,附加调试器后代码 运行 变慢已经不是什么新闻了,但看到它 运行 慢得多令人惊讶。
所以我学到的教训是:尽量不要过度分析,当涉及到时间时,偶尔关闭调试器。