.net 6+ 中用 smtpclient 发邮件能运行但默认配置必失败,核心在于端口、enablessl、凭据和发件人必须严格匹配服务商要求:推荐 port=587 + enablessl=true(触发 starttls),from 与 credentials 用户名须一致,附件需手动处理流、编码及大小校验。

直接用 SmtpClient 发邮件在 .NET 6+ 上能跑,但默认配置几乎必失败——不是连不上,就是被拒收、进垃圾箱,或附件乱码。核心问题不在“会不会写”,而在端口、EnableSsl、凭据和发件人三者是否严格匹配服务商要求。
Port = 587 + EnableSsl = true 是当前最稳组合
别再用 Port = 25 或 Port = 465 配 EnableSsl = true:前者明文已被主流服务商拒绝;后者在 .NET Core 3.0+ 中已不被推荐,QQ 邮箱、Gmail、Outlook 均明确建议走 587 + STARTTLS。
-
SmtpClient在 Port = 587 下设EnableSsl = true,实际触发的是 STARTTLS 协商(不是 SSL/TLS 直连),这是现代 SMTP 的标准流程 - 设
EnableSsl = false+ Port = 587?连接会建立,但认证阶段大概率被服务器中断并返回"5.7.0 Authentication Required" - 用 Port = 465 时,
EnableSsl = true才可能成功,但 Outlook 已弃用该端口,Gmail 仅保留兼容,不保证长期可用 - QQ 邮箱官方文档明确标注:SMTP 服务器为
smtp.qq.com,端口为587,必须启用 TLS
From 地址和 Credentials 必须对得上,否则 530 拒绝认证
SmtpClient.Credentials 决定能不能登录,MailMessage.From 决定邮件能不能投递——两者不一致,多数服务商直接拒收或标记为垃圾邮件。
- QQ 邮箱强制要求
MailMessage.From.Address和NetworkCredential.UserName完全相同(如都填user@qq.com) - 腾讯企业邮、阿里云企业邮允许发信别名,但需后台提前开通,且
UserName仍须是主账号邮箱 - 别用
UseDefaultCredentials = true——它传的是 Windows 登录凭据,SMTP 服务器根本不认 - Gmail 要求开启两步验证,并生成「应用专用密码」作为
NetworkCredential.Password,不能用登录密码
带附件时流必须活着,中文名必须编码
附件发不出去,90% 是因为流提前关闭,或文件名没按 RFC 2231 编码——Attachment 构造函数不会自动帮你处理这些。
- 传
FileStream给Attachment时,SmtpClient.Send()不负责关闭它,你得确保流在整个发送过程中保持打开状态 - 推荐写法:
using var stream = File.OpenRead(path);包裹整个MailMessage创建 +SmtpClient.Send()流程,而不是只包new Attachment(stream)那一行 - 内存中生成的
MemoryStream发送前务必.Position = 0,否则附件内容为空 - 中文附件名必须显式设置:
attachment.ContentType.Name = "报告.pdf"; attachment.NameEncoding = Encoding.UTF8;,否则 Outlook 显示=?utf-8?B?5byg5LiJLnBkZg==?=这类编码串
超时、大附件、编译警告这些细节真会卡上线
本地测试成功不等于生产可用。很多问题只在高并发、大文件、不同网络环境下暴露。
-
SmtpClient.Timeout默认是 100,000ms(100 秒),但 Gmail/QQ 邮箱通常 30 秒内就断连,建议显式设为30000 - 单个附件超过 25MB(Gmail)、50MB(Outlook)会静默失败——
Send()可能不抛异常,但收件方根本收不到。务必用new FileInfo(path).Length提前校验 -
SmtpClient在 .NET 6+ 已标记为[Obsolete],但微软至今没提供内置替代品。硬切MailKit前要意识到:它的SmtpClient.Connect()默认跳过证书验证,开发环境不报错,上线后常因RemoteCertificateValidationCallback缺失而握手失败 - 高频发送时别在循环里反复
new SmtpClient——它没连接池,Dispose()不及时会耗尽 socket











