SmtpException:Failure sending mail,无法连接SMTP服务器
结论
ErrorCode=10060 表示应用连接 125.63.68.148:25 时发生 TCP 连接超时。此时连接尚未建立,所以问题与邮件正文、收件人、身份验证和 SMTP 命令无关。
常见原因包括:
HostName配置了错误的服务器地址;- SMTP 服务没有监听该端口;
- 应用服务器、防火墙、云平台或运营商封锁了出站端口
25; - SMTP 服务器的防火墙没有放行应用服务器;
- DNS 将主机名解析到了错误或已经失效的 IP;
- 服务器要求使用
587 + STARTTLS,程序却仍在连接25; - 服务器只开放隐式 TLS 的
465端口,而经典System.Net.Mail.SmtpClient不支持这种连接方式。
反复修改端口解决不了配置不匹配的问题。HostName、PortNumber 和 EnableSsl 必须采用邮件服务提供方指定的同一组配置。
先确认实际连接目标
异常显示,程序最终尝试连接的是:
125.63.68.148:25
这表示当前 HostName 解析到了 125.63.68.148,PortNumber 是 25。先检查运行环境实际读取的配置,不要只看已经编辑过的 web.config:
string host = ConfigurationManager.AppSettings["HostName"];
string portValue = ConfigurationManager.AppSettings["PortNumber"];
if (string.IsNullOrWhiteSpace(host))
throw new ConfigurationErrorsException("HostName is missing.");
if (!int.TryParse(portValue, out int port))
throw new ConfigurationErrorsException("PortNumber is invalid.");
同时确认以下几项:
- 修改的是当前站点实际使用的
web.config; - 配置没有被父级配置或部署转换文件覆盖;
- 修改配置后,应用程序池已经重新加载;
HostName填写的是 SMTP 服务器,例如smtp.example.com,不是网站地址、收件人域名、邮箱地址或随意指定的 IP;- 该主机名是否确实应该解析到
125.63.68.148。
从应用所在服务器测试端口
必须在部署 ASP.NET 应用的服务器上测试。在开发电脑上能够连接,不代表 Web 服务器也能连接。
可以使用 PowerShell 直接测试目标端口:
Test-NetConnection 125.63.68.148 -Port 25
如果准备使用邮件服务商提供的主机名和 587 端口,可以测试:
Test-NetConnection smtp.example.com -Port 587
重点查看:
TcpTestSucceeded : True
如果结果是 False,或者连接一直超时,就需要检查网络或服务端:
- SMTP 服务是否正在目标端口监听;
- Windows 防火墙或网络出口防火墙是否允许该端口;
- SMTP 服务器是否只允许特定来源 IP;
- 云服务器或 ISP 是否封锁出站端口
25; - NAT、路由或安全组规则是否正确;
- DNS 是否解析到了正确地址。
也可以请邮件服务器管理员检查服务端日志,确认连接请求是否到达。如果服务端完全没有连接记录,问题通常出在客户端出口或中间网络。
正确选择端口和 SSL 模式
具体配置取决于 SMTP 服务器支持的协议:
| 端口 | 常见用途 | EnableSsl | 说明 |
|---|---|---|---|
25 | SMTP 中继或服务器间传输 | 视服务器而定 | 经常受到网络服务商限制,不建议直接用作公网客户端提交端口 |
587 | 邮件客户端提交 | true | 通常使用 STARTTLS,也是经典 SmtpClient 常用的安全配置 |
465 | 隐式 TLS/SMTPS | 不适合经典 SmtpClient | System.Net.Mail.SmtpClient 的 EnableSsl 使用 STARTTLS,不等于从建立连接开始就启用 TLS |
EnableSsl = true 并不表示可以在任意端口上强制加密。它要求服务器先接受普通 SMTP 连接,再通过 STARTTLS 升级连接。如果服务器只支持端口 465 的隐式 TLS,经典 System.Net.Mail.SmtpClient 通常无法正确连接,需要改用支持隐式 TLS 的邮件库,例如 MailKit。
使用 587 + STARTTLS 的配置示例
假设邮件提供方指定了以下配置:
- SMTP 主机:
smtp.example.com - 端口:
587 - 加密方式:STARTTLS
- 需要用户名和密码
web.config 可以写成:
<configuration>
<appSettings>
<add key="HostName" value="smtp.example.com" />
<add key="PortNumber" value="587" />
<add key="EnableSsl" value="true" />
<add key="SmtpUserName" value="pqr@example.com" />
<add key="SmtpPassword" value="replace-with-secure-secret" />
</appSettings>
</configuration>
发送代码如下:
using System;
using System.Configuration;
using System.Net;
using System.Net.Mail;
using System.Text;
var emailMessage = new StringBuilder();
emailMessage.Append("Dear Payment Team,");
emailMessage.Append("<br><br>Please find the Payment instruction.");
string host = ConfigurationManager.AppSettings["HostName"];
int port = int.Parse(
ConfigurationManager.AppSettings["PortNumber"]);
bool enableSsl = bool.Parse(
ConfigurationManager.AppSettings["EnableSsl"]);
string userName =
ConfigurationManager.AppSettings["SmtpUserName"];
string password =
ConfigurationManager.AppSettings["SmtpPassword"];
using (var message = new MailMessage())
{
message.From = new MailAddress("pqr@example.com");
message.To.Add("abc@gmail.com");
message.CC.Add("zcd@gmail.com");
message.Subject = "Timesheet Payment Instruction updated";
message.IsBodyHtml = true;
message.Body = emailMessage.ToString();
using (var smtp = new SmtpClient(host, port))
{
smtp.EnableSsl = enableSsl;
smtp.UseDefaultCredentials = false;
smtp.Credentials = new NetworkCredential(userName, password);
smtp.Timeout = 30000;
smtp.Send(message);
}
}
账号和密码不应长期以明文形式保存在源码或公开配置文件中。实际项目应使用受保护的配置、环境变量或密钥管理服务。
使用内部 SMTP Relay 的配置示例
如果公司提供内部邮件中继,并明确要求使用端口 25、不启用 TLS、根据来源 IP 授权,可以这样配置:
<appSettings>
<add key="HostName" value="smtp-relay.internal.example.com" />
<add key="PortNumber" value="25" />
<add key="EnableSsl" value="false" />
</appSettings>
using (var smtp = new SmtpClient(
ConfigurationManager.AppSettings["HostName"],
int.Parse(ConfigurationManager.AppSettings["PortNumber"])))
{
smtp.EnableSsl = false;
smtp.UseDefaultCredentials = true;
smtp.Send(message);
}
是否允许匿名发送、是否使用 Windows 凭据,以及允许使用哪些发件人地址,都应以公司 SMTP 管理员提供的规则为准。
如果发件人确实是 Gmail
如果需要通过 Gmail 的 SMTP 服务发送邮件,HostName 不应填写任意服务器或收件人的邮件服务器,而应使用 Gmail 提供的 SMTP 主机。常见的 STARTTLS 配置是:
<add key="HostName" value="smtp.gmail.com" />
<add key="PortNumber" value="587" />
<add key="EnableSsl" value="true" />
代码还需要提供有效的 Gmail 身份验证信息:
smtp.UseDefaultCredentials = false;
smtp.Credentials = new NetworkCredential(
"pqr@gmail.com",
"application-specific-credential");
smtp.EnableSsl = true;
是否可以使用应用专用密码、是否必须使用 OAuth,以及账号是否允许 SMTP 登录,取决于该 Google 账号和组织管理员的安全策略。普通登录密码不一定能直接用于 SMTP。
将 Msg.From 设置为 Gmail 地址,并不会让程序自动通过 Gmail 发送邮件。程序实际使用哪个邮件系统,只取决于 smtp.Host 的设置。
改进异常记录
当前代码只输出最外层异常,不便于定位问题。应完整记录内部异常,同时记录主机、端口和 SSL 设置,但不要记录密码:
catch (SmtpException ex)
{
Console.WriteLine(
"SMTP failed. Host={0}, Port={1}, EnableSsl={2}, StatusCode={3}",
host,
port,
enableSsl,
ex.StatusCode);
Console.WriteLine(ex.ToString());
}
在 ASP.NET 应用中,Console.WriteLine 的内容通常不会显示在浏览器中,也不容易在日志里找到。最好使用项目现有的日志框架或 Windows Event Log。
判断问题处于哪一层
可以按照以下顺序排查:
Test-NetConnection失败或超时:检查网络、防火墙、安全组、路由、端口和 SMTP 服务监听状态。- TCP 连接成功,但出现 TLS 错误:检查端口和加密模式是否匹配,以及 TLS 协议或证书是否有问题。
- 出现
5.7.x、authentication failed 等响应:检查账号、密码、OAuth 和服务器授权策略。 - 身份验证成功,但服务器拒绝发件人:检查
From地址是否有发送权限,以及服务器是否禁止代发。 - 服务器已接受邮件,但收件人没有收到:检查退信、垃圾邮件、邮件队列以及 SPF、DKIM、DMARC。这些不是当前
10060超时的直接原因。
当前异常属于第一种情况。先确认应用服务器能够连接到正确的 SMTP 主机和端口,再处理 EnableSsl、凭据和发件人权限。延长 SmtpClient.Timeout 只会让程序等待更久,无法解决端口被阻断、地址错误或服务未监听的问题。
备注:内容仅供参考。
