如何使用 .NET Framework 通过 SSL SMTP 发送电子邮件?
Posted
技术标签:
【中文标题】如何使用 .NET Framework 通过 SSL SMTP 发送电子邮件?【英文标题】:How can I send emails through SSL SMTP with the .NET Framework? 【发布时间】:2010-11-03 22:02:37 【问题描述】:.NET Framework 有没有办法通过端口 465 上的 SSL SMTP 服务器发送电子邮件?
通常的方式:
System.Net.Mail.SmtpClient _SmtpServer = new System.Net.Mail.SmtpClient("tempurl.org");
_SmtpServer.Port = 465;
_SmtpServer.EnableSsl = true;
_SmtpServer.Credentials = new System.Net.NetworkCredential("username", "password");
_SmtpServer.Timeout = 5000;
_SmtpServer.UseDefaultCredentials = false;
MailMessage mail = new MailMessage();
mail.From = new MailAddress(from);
mail.To.Add(to);
mail.CC.Add(cc);
mail.Subject = subject;
mail.Body = content;
mail.IsBodyhtml = useHtml;
_SmtpServer.Send(mail);
超时:
System.Net Verbose: 0 : [1024] SmtpClient::.ctor(host=ssl0.ovh.net, port=465)
System.Net Information: 0 : [1024] Associating SmtpClient#64923656 with SmtpTransport#44624228
System.Net Verbose: 0 : [1024] Exiting SmtpClient::.ctor() -> SmtpClient#64923656
System.Net Information: 0 : [1024] Associating MailMessage#17654054 with Message#52727599
System.Net Verbose: 0 : [1024] SmtpClient#64923656::Send(MailMessage#17654054)
System.Net Information: 0 : [1024] SmtpClient#64923656::Send(DeliveryMethod=Network)
System.Net Information: 0 : [1024] Associating SmtpClient#64923656 with MailMessage#17654054
System.Net Information: 0 : [1024] Associating SmtpTransport#44624228 with SmtpConnection#14347911
System.Net Information: 0 : [1024] Associating SmtpConnection#14347911 with ServicePoint#51393439
System.Net.Sockets Verbose: 0 : [1024] Socket#26756241::Socket(InterNetwork#2)
System.Net.Sockets Verbose: 0 : [1024] Exiting Socket#26756241::Socket()
System.Net.Sockets Verbose: 0 : [1024] Socket#23264094::Socket(InterNetworkV6#23)
System.Net.Sockets Verbose: 0 : [1024] Exiting Socket#23264094::Socket()
System.Net.Sockets Verbose: 0 : [1024] Socket#26756241::Connect(20:465#337754884)
System.Net.Sockets Verbose: 0 : [1024] Exiting Socket#26756241::Connect()
System.Net.Sockets Verbose: 0 : [1024] Socket#23264094::Close()
System.Net.Sockets Verbose: 0 : [1024] Socket#23264094::Dispose()
System.Net.Sockets Verbose: 0 : [1024] Exiting Socket#23264094::Close()
System.Net Information: 0 : [1024] Associating SmtpConnection#14347911 with SmtpPooledStream#14303791
System.Net.Sockets Verbose: 0 : [1024] Socket#26756241::Receive()
System.Net.Sockets Verbose: 0 : [2404] Socket#26756241::Dispose()
System.Net.Sockets Error: 0 : [1024] Exception in the Socket#26756241::Receive - A blocking operation was interrupted by a call to WSACancelBlockingCall
System.Net.Sockets Verbose: 0 : [1024] Exiting Socket#26756241::Receive() -> 0#0
System.Net Error: 0 : [1024] Exception in the SmtpClient#64923656::Send - Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall.
System.Net Error: 0 : [1024] at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.DelegatedStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.BufferedReadStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.Mail.SmtpReplyReaderFactory.ReadLines(SmtpReplyReader caller, Boolean oneLine)
at System.Net.Mail.SmtpReplyReaderFactory.ReadLine(SmtpReplyReader caller)
at System.Net.Mail.SmtpConnection.GetConnection(String host, Int32 port)
at System.Net.Mail.SmtpTransport.GetConnection(String host, Int32 port)
at System.Net.Mail.SmtpClient.GetConnection()
at System.Net.Mail.SmtpClient.Send(MailMessage message)
System.Net Verbose: 0 : [1024] Exiting SmtpClient#64923656::Send()
System.Net Information: 0 : [1024] Associating MailMessage#49584532 with Message#19699911
我四处搜索,发现 System.Net.Mail 支持端口 587 上的连接(显式 SSL 的默认端口,开始未加密,然后发出 STARTDLS,然后切换到加密连接:RFC 2228),但不支持隐式 SSL (整个连接都包裹在 SSL 层中)...
【问题讨论】:
SmtpClient.EnableSsl Property "另一种连接方法是在发送任何协议命令之前预先建立 SSL 会话。这种连接方法有时称为 SMTP/SSL、SMTP over SSL 或 SMTPS,并且通过默认使用端口 465。目前不支持这种使用 SSL 的替代连接方法。” FWIW 【参考方案1】:它适用于 System.Web.Mail(标记为过时):
private const string SMTP_SERVER = "http://schemas.microsoft.com/cdo/configuration/smtpserver";
private const string SMTP_SERVER_PORT = "http://schemas.microsoft.com/cdo/configuration/smtpserverport";
private const string SEND_USING = "http://schemas.microsoft.com/cdo/configuration/sendusing";
private const string SMTP_USE_SSL = "http://schemas.microsoft.com/cdo/configuration/smtpusessl";
private const string SMTP_AUTHENTICATE = "http://schemas.microsoft.com/cdo/configuration/smtpauthenticate";
private const string SEND_USERNAME = "http://schemas.microsoft.com/cdo/configuration/sendusername";
private const string SEND_PASSWORD = "http://schemas.microsoft.com/cdo/configuration/sendpassword";
System.Web.Mail.MailMessage mail = new System.Web.Mail.MailMessage();
mail.Fields[SMTP_SERVER] = "tempurl.org";
mail.Fields[SMTP_SERVER_PORT] = 465;
mail.Fields[SEND_USING] = 2;
mail.Fields[SMTP_USE_SSL] = true;
mail.Fields[SMTP_AUTHENTICATE] = 1;
mail.Fields[SEND_USERNAME] = "username";
mail.Fields[SEND_PASSWORD] = "password";
System.Web.Mail.SmtpMail.Send(mail);
您对过时的命名空间使用有何看法?
【讨论】:
这个答案可以改进。这里没有 to、from、subject 或 body。也就是说,您错过了整个电子邮件部分。当然,不难,但为什么不把所有东西都放在一个地方呢?【参考方案2】:这是一个如何通过同样使用 SSL/465 的 GMail 发送电子邮件的示例。对下面的代码稍作调整应该可以工作!
using System.Web.Mail;
using System;
public class MailSender
public static bool SendEmail(
string pGmailEmail,
string pGmailPassword,
string pTo,
string pSubject,
string pBody,
System.Web.Mail.MailFormat pFormat,
string pAttachmentPath)
try
System.Web.Mail.MailMessage myMail = new System.Web.Mail.MailMessage();
myMail.Fields.Add
("http://schemas.microsoft.com/cdo/configuration/smtpserver",
"smtp.gmail.com");
myMail.Fields.Add
("http://schemas.microsoft.com/cdo/configuration/smtpserverport",
"465");
myMail.Fields.Add
("http://schemas.microsoft.com/cdo/configuration/sendusing",
"2");
//sendusing: cdoSendUsingPort, value 2, for sending the message using
//the network.
//smtpauthenticate: Specifies the mechanism used when authenticating
//to an SMTP
//service over the network. Possible values are:
//- cdoAnonymous, value 0. Do not authenticate.
//- cdoBasic, value 1. Use basic clear-text authentication.
//When using this option you have to provide the user name and password
//through the sendusername and sendpassword fields.
//- cdoNTLM, value 2. The current process security context is used to
// authenticate with the service.
myMail.Fields.Add
("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate","1");
//Use 0 for anonymous
myMail.Fields.Add
("http://schemas.microsoft.com/cdo/configuration/sendusername",
pGmailEmail);
myMail.Fields.Add
("http://schemas.microsoft.com/cdo/configuration/sendpassword",
pGmailPassword);
myMail.Fields.Add
("http://schemas.microsoft.com/cdo/configuration/smtpusessl",
"true");
myMail.From = pGmailEmail;
myMail.To = pTo;
myMail.Subject = pSubject;
myMail.BodyFormat = pFormat;
myMail.Body = pBody;
if (pAttachmentPath.Trim() != "")
MailAttachment MyAttachment =
new MailAttachment(pAttachmentPath);
myMail.Attachments.Add(MyAttachment);
myMail.Priority = System.Web.Mail.MailPriority.High;
System.Web.Mail.SmtpMail.SmtpServer = "smtp.gmail.com:465";
System.Web.Mail.SmtpMail.Send(myMail);
return true;
catch (Exception ex)
throw;
【讨论】:
System.Web.Mail 已弃用。 我库中的旧代码!因此,声明“对下面的代码进行微调应该可以工作!” 这是否适用于 .Net 4.0 之前的版本? 基于link 你是对的,这不是错误,而是设计使然。 SMTP 有两种类型的 SSL 身份验证,我们仅支持 System.Net.Mail 中的一种(根据设计)——显式 SSL。并且只有 Web.Mail 可以发送 ssl 邮件 现在似乎这是 Gmail 的一项要求?这个解决方案在 2017 年解决了我的问题!【参考方案3】:如果是隐式 SSL,它似乎无法使用 System.Net.Mail 完成,目前尚不支持。
http://blogs.msdn.com/webdav_101/archive/2008/06/02/system-net-mail-with-ssl-to-authenticate-against-port-465.aspx
要检查它是否是隐式 SSL,请尝试 this.
【讨论】:
【参考方案4】:您也可以通过端口 465 进行连接,但由于 System.Net.Mail 命名空间的一些限制,您可能必须更改您的代码。这是因为命名空间不提供建立隐式 SSL 连接的能力。这在http://blogs.msdn.com/b/webdav_101/archive/2008/06/02/system-net-mail-with-ssl-to-authenticate-against-port-465.aspx 进行了讨论。
无需使用现已过时的 System.Web.Mail 命名空间即可建立隐式连接,但您必须访问 Microsoft CDO(协作数据对象)。我在另一个讨论中提供了一个如何使用 CDO 的示例 (GMail SMTP via C# .Net errors on all ports)。
希望这会有所帮助!
【讨论】:
【参考方案5】:如果对此代码有任何疑问,请提出您的问题(这里的 gmail 端口号是 587)
// code to Send Mail
// Add following Lines in your web.config file
// <system.net>
// <mailSettings>
// <smtp>
// <network host="smtp.gmail.com" port="587" userName="xxx@gmail.com" password="yyy" defaultCredentials="false"/>
// </smtp>
// </mailSettings>
// </system.net>
// Add below lines in your config file inside appsetting tag <appsetting></appsetting>
// <add key="emailFromAddress" value="xxxx@gmail.com"/>
// <add key="emailToAddress" value="xxxxxxx@gmail.com"/>
// <add key="EmailSsl" value="true"/>
// Namespace Used
using System.Net.Mail;
public static bool SendingMail(string subject, string content)
// getting the values from config file through c#
string fromEmail = ConfigurationSettings.AppSettings["emailFromAddress"];
string mailid = ConfigurationSettings.AppSettings["emailToAddress"];
bool useSSL;
if (ConfigurationSettings.AppSettings["EmailSsl"] == "true")
useSSL = true;
else
useSSL = false;
SmtpClient emailClient;
MailMessage message;
message = new MailMessage();
message.From = new MailAddress(fromEmail);
message.ReplyTo = new MailAddress(fromEmail);
if (SetMailAddressCollection(message.To, mailid))
message.Subject = subject;
message.Body = content;
message.IsBodyHtml = true;
emailClient = new SmtpClient();
emailClient.EnableSsl = useSSL;
emailClient.Send(message);
return true;
// if you are sending mail in group
private static bool SetMailAddressCollection(MailAddressCollection toAddresses, string mailId)
bool successfulAddressCreation = true;
toAddresses.Add(new MailAddress(mailId));
return successfulAddressCreation;
【讨论】:
问题是关于使用端口 465,但您的示例是针对端口 587。存在显着差异。【参考方案6】:尝试检查这个免费的开源替代品 https://www.nuget.org/packages/AIM 它是免费使用和开源的,并且使用与 System.Net.Mail 完全相同的方式 要将电子邮件发送到隐式 ssl 端口,您可以使用以下代码
public static void SendMail()
var mailMessage = new MimeMailMessage();
mailMessage.Subject = "test mail";
mailMessage.Body = "hi dude!";
mailMessage.Sender = new MimeMailAddress("you@gmail.com", "your name");
mailMessage.To.Add(new MimeMailAddress("yourfriend@gmail.com", "your friendd's name"));
// You can add CC and BCC list using the same way
mailMessage.Attachments.Add(new MimeAttachment("your file address"));
//Mail Sender (Smtp Client)
var emailer = new SmtpSocketClient();
emailer.Host = "your mail server address";
emailer.Port = 465;
emailer.SslType = SslMode.Ssl;
emailer.User = "mail sever user name";
emailer.Password = "mail sever password" ;
emailer.AuthenticationMode = AuthenticationType.Base64;
// The authentication types depends on your server, it can be plain, base 64 or none.
//if you do not need user name and password means you are using default credentials
// In this case, your authentication type is none
emailer.MailMessage = mailMessage;
emailer.OnMailSent += new SendCompletedEventHandler(OnMailSent);
emailer.SendMessageAsync();
// A simple call back function:
private void OnMailSent(object sender, AsyncCompletedEventArgs asynccompletedeventargs)
if (e.UserState!=null)
Console.Out.WriteLine(e.UserState.ToString());
if (e.Error != null)
MessageBox.Show(e.Error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
else if (!e.Cancelled)
MessageBox.Show("Send successfull!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
【讨论】:
您能提供此 AIM 的文档吗? ? ?在 sourceforge 上找不到任何文档或网站??? 最好的方法是下载示例应用程序和代码【参考方案7】:我参加这个聚会迟到了,但我会为任何可能对替代方案感兴趣的路人提供我的方法。
如前面的答案所述,System.Net.Mail
SmtpClient
类不支持隐式 SSL。它确实支持显式 SSL,这需要通过端口 25 与 SMTP 服务器建立不安全的连接,以便协商传输级别安全性 (TLS)。我用这种微妙的here 在博客上记录了我的艰辛。
简而言之,通过隐式 SSL 端口 465 的 SMTP 要求在连接到 SMTP 服务器之前协商 TLS。我没有编写 .Net SMTPS 实现,而是转向了一个名为 Stunnel 的实用程序。这是一项小型服务,可让您通过 SSL 将本地端口上的流量重定向到远程端口。
免责声明:Stunnel 使用 OpenSSL 库的部分内容,该库最近在所有主要科技新闻媒体上发布了一个备受瞩目的漏洞利用。我相信最新版本使用了修补过的 OpenSSL,但请自行承担使用风险。
安装实用程序后,在配置文件中添加一小部分内容:
; Example SSL client mode services
[my-smtps]
client = yes
accept = 127.0.0.1:465
connect = mymailserver.com:465
...指示 Stunnel 服务将本地请求重新路由到端口 465 到我在端口 465 上的邮件服务器。这发生在 TLS 上,它满足另一端的 SMTP 服务器。
使用此实用程序,以下代码将成功通过端口 465 传输:
using System;
using System.Net;
using System.Net.Mail;
namespace RSS.SmtpTest
class Program
static void Main( string[] args )
try
using( SmtpClient smtpClient = new SmtpClient( "localhost", 465 ) ) // <-- note the use of localhost
NetworkCredential creds = new NetworkCredential( "username", "password" );
smtpClient.Credentials = creds;
MailMessage msg = new MailMessage( "joe@schmoe.com", "jane@schmoe.com", "Test", "This is a test" );
smtpClient.Send( msg );
catch( Exception ex )
Console.WriteLine( ex.Message );
所以这里的优点是您可以使用隐式 SSL 和端口 465 作为安全协议,同时仍然使用框架中内置的发送邮件方法。缺点是它需要使用第三方服务,除了这个特定的功能之外,它可能对任何东西都没有用处。
【讨论】:
谢谢鲍勃。令人难以置信的是,在 2019 年,这仍然是最优雅的解决方案。使用已弃用的 System.Web.Mail.MailMessage 是不可接受的,更不用说知道您必须确保默认输入语言是 en-US,否则您会收到“COMException: The requested body part is not found in this消息”错误(顺便说一句,应该会自动触发 WTF?反应 ;-) @StephaneEhret 我同意。正如我在博客文章中所指出的,我打算编写一个隐式 SSL .Net SMTP 库,但一直没能找到时间。有一些可用的库支持隐式 SSL,但其中大多数都不适合我。也许我会重新审视这个项目。【参考方案8】:对于 gmail,这些设置对我有用,ServicePointManager.SecurityProtocol 行是必要的。因为我设置了两步验证,所以我需要从google app password generator 获取应用密码。
SmtpClient mailer = new SmtpClient();
mailer.Host = "smtp.gmail.com";
mailer.Port = 587;
mailer.EnableSsl = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
【讨论】:
警告:SecurityProtocolType.Tls
仅表示 TLS 1.0。如果您想允许其他协议(截至 2021 年 10 月,Microsoft 已停止使用 TLS 1.0),您也需要包含它们。如果您的目标是 .Net 4.8 或更高版本,则可以使用 SecurityProtocolType.SystemDefault
以便操作系统选择最新协议(或一组作为系统默认协议)。【参考方案9】:
在 VB.NET 中尝试连接到 465 上的 Rackspace 的 SSL 端口时,我遇到了同样的问题(需要隐式 SSL)。为了成功连接,我使用了https://www.nuget.org/packages/MailKit/。
以下是 HTML 电子邮件的示例。
Imports MailKit.Net.Smtp
Imports MailKit
Imports MimeKit
Sub somesub()
Dim builder As New BodyBuilder()
Dim mail As MimeMessage
mail = New MimeMessage()
mail.From.Add(New MailboxAddress("", c_MailUser))
mail.To.Add(New MailboxAddress("", c_ToUser))
mail.Subject = "Mail Subject"
builder.HtmlBody = "<html><body>Body Text"
builder.HtmlBody += "</body></html>"
mail.Body = builder.ToMessageBody()
Using client As New SmtpClient
client.Connect(c_MailServer, 465, True)
client.AuthenticationMechanisms.Remove("XOAUTH2") ' Do not use OAUTH2
client.Authenticate(c_MailUser, c_MailPassword) ' Use a username / password to authenticate.
client.Send(mail)
client.Disconnect(True)
End Using
End Sub
【讨论】:
这对我很有帮助。它是无法处理隐式 SSL 的SmtpClient
的一个很好的替代方案。此外,它比 AIM(在另一个答案中建议)成熟得多,并且可以与 UTF-8(和其他编码)一起正常工作。谢谢。【参考方案10】:
我知道我加入讨论的时间很晚,但我认为这对其他人可能有用。
我想避免被弃用的东西,经过大量的摆弄,我找到了一种简单的方法来发送到需要隐式 SSL 的服务器:使用 NuGet 并将 MailKit package 添加到项目中。 (我使用了针对 .NET 4.6.2 的 VS2017,但它应该适用于较低的 .NET 版本...)
那么你只需要这样做:
using MailKit.Net.Smtp;
using MimeKit;
var client = new SmtpClient();
client.Connect("server.name", 465, true);
// Note: since we don't have an OAuth2 token, disable the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove ("XOAUTH2");
if (needsUserAndPwd)
// Note: only needed if the SMTP server requires authentication
client.Authenticate (user, pwd);
var msg = new MimeMessage();
msg.From.Add(new MailboxAddress("sender@ema.il"));
msg.To .Add(new MailboxAddress("target@ema.il"));
msg.Subject = "This is a test subject";
msg.Body = new TextPart("plain")
Text = "This is a sample message body"
;
client.Send(msg);
client.Disconnect(true);
当然,您也可以调整它以使用显式 SSL 或根本不使用传输安全性。
【讨论】:
我正在使用基于您上述代码的代码通过 mailkit 发送电子邮件,但出现错误,您能看到这里吗,我会很高兴您。 ***.com/questions/46421323/… 我回复了您的帖子,然后添加了更多选项.. 但您的问题似乎有点过于宽泛,无法得到简单的答案,除非您尝试排除一些可能的原因..【参考方案11】:正如评论中所述
http://blogs.msdn.com/webdav_101/archive/2008/06/02/system-net-mail-with-ssl-to-authenticate-against-port-465.aspx
对于 System.Net.Mail,使用端口 25 而不是 465:
您必须设置 SSL=true 和 Port=25。服务器响应来自未受保护的 25 的请求,然后抛出与受保护的 465 的连接。
【讨论】:
我需要 -1 这个。引用的博客页面有一个对此的评论引用,没有确认。该评论指的是另一个没有任何确认说明的俄语博客。直到有一些确认这是一个事实解决方案,到目前为止,它是一个人跟着一个人跟着一个人......这对于一个 SO 答案来说还不够好。 附录:这个答案描述了在端口 x 上连接,然后被扔到端口 y。这是 STARTTLS 的定义,OP 说他不想要。 “System.Net.Mail 命名空间仅支持显式 SSL。显式 SSL 在端口 25 上以未加密方式启动,然后发出 STARTTLS 信号,然后切换到加密连接。” - 参考:support.microsoft.com/en-us/help/950260/… 我已验证 System.Net.Mail.SmtpClient 确实支持 STARTTLS 的端口 587 连接。我在 25 日无法连接。【参考方案12】:为了扩展Idriss 的答案,这是我的静态方法,它的工作原理就像一个冠军:
using System.Web.Mail;
public static void SendEmail(String Subject, String sMessage, String EmailAddress, String STMPServer, int SMTPPort, String UserName = "", String Password = null, Boolean IsSecure = true, Boolean IsHTML = true, String FromAddress = "no-reply@contoso.com")
string SMTP_SERVER = "http://schemas.microsoft.com/cdo/configuration/smtpserver";
string SMTP_SERVER_PORT = "http://schemas.microsoft.com/cdo/configuration/smtpserverport";
string SEND_USING = "http://schemas.microsoft.com/cdo/configuration/sendusing";
string SMTP_USE_SSL = "http://schemas.microsoft.com/cdo/configuration/smtpusessl";
string SMTP_AUTHENTICATE = "http://schemas.microsoft.com/cdo/configuration/smtpauthenticate";
string SEND_USERNAME = "http://schemas.microsoft.com/cdo/configuration/sendusername";
string SEND_PASSWORD = "http://schemas.microsoft.com/cdo/configuration/sendpassword";
MailMessage mail = new MailMessage();
mail.Fields[SMTP_SERVER] = STMPServer;
mail.Fields[SMTP_SERVER_PORT] = SMTPPort;
mail.Fields[SEND_USING] = 2;
mail.Fields[SMTP_USE_SSL] = IsSecure;
if(!String.IsNullOrEmpty(UserName) && !String.IsNullOrEmpty(Password))
mail.Fields[SMTP_AUTHENTICATE] = 1;
mail.Fields[SEND_USERNAME] = UserName;
mail.Fields[SEND_PASSWORD] = Password;
else
mail.Fields[SMTP_AUTHENTICATE] = 0;
mail.From = FromAddress;
mail.To = EmailAddress;
mail.Subject = Subject;
if (IsHTML)
mail.BodyFormat = MailFormat.Html;
else
mail.BodyFormat = MailFormat.Text;
mail.Body = sMessage;
SmtpMail.Send(mail);
【讨论】:
以上是关于如何使用 .NET Framework 通过 SSL SMTP 发送电子邮件?的主要内容,如果未能解决你的问题,请参考以下文章
如何通过安装包将 C# Dll 放入工具箱和 .NET Framework 组件中?
如何通过 Windows Azure(预览版)管理门户设置 ADO.NET Entity Framework 连接字符串?