PAYATHON 2026

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,或者连接一直超时,就需要检查网络或服务端:

  1. SMTP 服务是否正在目标端口监听;
  2. Windows 防火墙或网络出口防火墙是否允许该端口;
  3. SMTP 服务器是否只允许特定来源 IP;
  4. 云服务器或 ISP 是否封锁出站端口 25;
  5. NAT、路由或安全组规则是否正确;
  6. DNS 是否解析到了正确地址。

也可以请邮件服务器管理员检查服务端日志,确认连接请求是否到达。如果服务端完全没有连接记录,问题通常出在客户端出口或中间网络。

正确选择端口和 SSL 模式

具体配置取决于 SMTP 服务器支持的协议:

端口常见用途EnableSsl说明
25SMTP 中继或服务器间传输视服务器而定经常受到网络服务商限制,不建议直接用作公网客户端提交端口
587邮件客户端提交true通常使用 STARTTLS,也是经典 SmtpClient 常用的安全配置
465隐式 TLS/SMTPS不适合经典 SmtpClientSystem.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。

判断问题处于哪一层

可以按照以下顺序排查:

  1. Test-NetConnection 失败或超时:检查网络、防火墙、安全组、路由、端口和 SMTP 服务监听状态。
  2. TCP 连接成功,但出现 TLS 错误:检查端口和加密模式是否匹配,以及 TLS 协议或证书是否有问题。
  3. 出现 5.7.x、authentication failed 等响应:检查账号、密码、OAuth 和服务器授权策略。
  4. 身份验证成功,但服务器拒绝发件人:检查 From 地址是否有发送权限,以及服务器是否禁止代发。
  5. 服务器已接受邮件,但收件人没有收到:检查退信、垃圾邮件、邮件队列以及 SPF、DKIM、DMARC。这些不是当前 10060 超时的直接原因。

当前异常属于第一种情况。先确认应用服务器能够连接到正确的 SMTP 主机和端口,再处理 EnableSsl、凭据和发件人权限。延长 SmtpClient.Timeout 只会让程序等待更久,无法解决端口被阻断、地址错误或服务未监听的问题。

备注:内容仅供参考。