在.NET框架中发送电子邮件及常见问题 – 使用 C# 代码






4.84/5 (27投票s)
本文解释了在 .NET 框架中发送电子邮件的核心概念,以及新的 .NET 开发人员在使用它们时面临的问题,并提供了一些解决基本错误的技巧。
大多数时候,新开发人员在 .NET 框架中遇到一个非常简单的任务:“发送电子邮件”。好吧,我这里不打算谈论任何语言(C# 或 VB.NET,Visual C++ 也包括在内),我将普遍谈论 .NET 框架以及 .NET 框架公开的用于发送电子邮件的程序集,使用您自己的 SMTP 服务器设置,例如用户名/密码组合、端口号,以及(最重要的是)您的 SMTP 服务器的主机名。
背景
电子邮件是电子邮箱的简称,它们在日常通信中被广泛使用,您可以发送电子邮件分享文本数据,也可以轻松地通过电子邮件发送您的相册。电子邮件在很长一段时间内一直是互联网娱乐的一部分,人们使用许多不同的电子邮件客户端,有些人喜欢在线客户端,例如 Gmail、Yahoo! 等,有些人则喜欢离线版本的电子邮件客户端,它们使用互联网连接从服务器下载电子邮件,例如 Thunderbird、Outlook 等。
但事实是,它们都使用相同的协议在互联网网络上传输电子邮件。在这篇博客文章中,我将讨论通过网络发送电子邮件,下载电子邮件是一个完全独立的主题,并且会有一个独立的协议在后端工作来从服务器下载电子邮件。
发送电子邮件
电子邮件通过互联网使用 SMTP 协议发送。它类似于超文本协议(不是在通信方式上,而是在作为通信协议的方面)。有关 SMTP 的更多信息,您可以阅读维基百科页面,我在这里不会深入探讨该协议,而是将只详细阐述在 .NET 框架中发送电子邮件的方法。
.NET 框架能为我提供什么?
.NET 框架(谁会对此一无所知呢?)拥有一堆很酷的程序集,我们可以用我们喜欢的语言(从 C# 到 C++)进行开发,.NET 框架中的程序集让我们能够专注于应用程序的质量和逻辑,将其他底层编码留给框架本身,包括最特别的垃圾回收和内存管理等。
.NET 框架有一个命名空间,称为 System.Net。此命名空间负责 .NET 应用程序的网络通信。但我们将更关注 System.Net.Mail 命名空间,用于处理邮件协议,它公开了 SmtpClient、MailMessage 类,以便我们轻松地将数据传递给对象,并使用 .NET 框架发送电子邮件。
创建发送电子邮件的模块
由于 .NET 框架提供了许多框架来创建您的应用程序,从最基本的控制台应用程序,到用户友好的Windows Presentation Foundation。有趣的是,在 .NET 框架中,相同的代码可以在控制台应用程序和 WPF 应用程序的后端使用。因此,在控制台应用程序中用于发送电子邮件的代码与您在 WPF 应用程序中使用的代码完全相同。这就是为什么我不打算指定任何框架,而是将使用控制台应用程序作为我们的项目,因为它更容易理解,并且更专注于代码。您可以在自己的 IDE 中创建任何您想要的应用程序,从 Windows Forms 到 WPF 再到 Web 应用程序(使用 ASP.NET)。
应用程序创建完成后,您可以创建一个简单的模块(函数;不要与 VB.NET 的 Module 混淆)。在其中您可以编写以下代码,不用担心,我将在博客文章的下一部分解释代码。
// You should use a using statement
using (SmtpClient client = new SmtpClient("<smtp-server-address>", 25))
{
// Configure the client
client.EnableSsl = true;
client.Credentials = new NetworkCredential("<username>", "<password>");
// client.UseDefaultCredentials = true;
// A client has been created, now you need to create a MailMessage object
MailMessage message = new MailMessage(
"from@example.com", // From field
"to@example.com", // Recipient field
"Hello", // Subject of the email message
"World!" // Email message body
);
// Send the message
client.Send(message);
/*
* Since I was using Console app, that is why I am able to use the Console
* object, your framework would have different ones.
* There is actually no need for these following lines, you can ignore them
* if you want to. SMTP protocol would still send the email of yours. */
// Print a notification message
Console.WriteLine("Email has been sent.");
// Just for the sake of pausing the application
Console.Read();
}
瞧,(如果您在上述代码中添加了正确的详细信息)您的电子邮件将毫无问题地发送到目的地;除了互联网连接问题。现在让我们将代码分解并理解发生了什么,之后我将提到一些在编程中出现并给新开发人员理解整个过程造成困扰的问题,其中包括由于连接问题等引起的错误。
以上代码的解释
代码的第一步是使用 using
语句。在 .NET 框架中,您会遇到使用资源的各种对象,这些资源需要正确地释放,或者至少关闭。例如,在创建文件时,需要在任何其他进程使用该文件之前调用 Close()
函数;同样,有些进程要求您调用 Dispose()
函数来释放所有资源。但是您可以使用 using
语句,让 .NET 框架自己处理所有需要调用这些函数的对象。例如,以下代码,
using (SmtpClient client = new SmtpClient()) {
// code here
}
比以下代码更好,
SmtpClient client = new SmtpClient();
// code here..
client.Dispose();
由于许多特定因素,我不会在这里讨论。这使得我们讨论 using
语句,您可以在 MSDN 文档中找到关于 using
语句的更多深入细节。
接下来是 SmtpClient
对象,SmtpClient
用于创建一个对象,该对象将在您的机器和您正在使用的 SMTP 服务器之间建立连接。SmtpClient
要求您在其中设置一些内容。
- 主机名,即您的 SMTP 服务器地址的字符串格式名称。
- 您将用于连接的端口,默认是 25 (TCP 端口)。
- 大多数连接都要求您启用 SSL。您也可以在我们的代码中看到这种情况。
- 在使用服务之前需要提供凭据,大多数服务器(Gmail、Outlook 等)要求您发送用户名/密码组合,以便使用 SMTP 协议从您的帐户发送电子邮件。这就是为什么在大多数情况下,默认凭据会导致开发人员遇到错误。我们使用 NetworkCredential 对象(来自 System.Net 命名空间)将我们的用户名/密码传递给服务器。
由于 SmtpClient
是可释放的,所以我们将其用在 using
语句中。我们即将发送一封电子邮件,为此我们创建一个名为 MailMessage
的对象,并将我们的数据传递给它。MailMessage
对象可以设置电子邮件消息的“发件人”、“收件人”、“主题”和“正文”字段,然后可以发送。您可以在我们的示例中看到,我们正在使用构造函数来创建 MailMessage
对象,该对象将保存我们“发件人”、“收件人”、“主题”和“正文”字段的数据。
最后,我们使用 Send()
函数发送电子邮件。有趣的是,在 WPF 或 Win Forms 等 GUI 框架中,我们应该使用 SendAsync
来实现应用程序的异步性,这将帮助我们为应用程序创建流畅的 GUI,否则应用程序将一直停滞,直到电子邮件发送完毕并且控制流从这行代码继续。要了解更多关于异步编程的信息,请访问 MSDN 链接并从那里学习更多,他们为像您这样的初学者提供了大量优秀内容。
编程中的一些错误
通常,开发人员总会遗漏一些错误,然后试图让自己困惑于“我到底在哪里遗漏了?”。同样,在发送电子邮件和建立安全连接时,通常会遇到很多问题,有些是语法问题,有些是逻辑问题,但我将讨论可能发生的连接错误。我尝试自己引发一些异常,以便在这里与您分享,让您了解这些异常何时可能对您的环境造成问题。
通常,连接中的异常仅在 Send
或 SendAsync
方法中引发,当 SmtpClient
无法成功发送您的电子邮件时。这可能是由于连接问题、身份验证问题或任何其他问题。
SMTP 主机名问题
一个常见问题可能是您传递给客户端连接的主机名,它必须是正确的,并且不带“http://”。您可能会遇到这样的问题,

主机名无法解析,因为它包含“http://”。如果您使用 Gmail 作为 SMTP 服务器,只需传递 smtp.gmail.com。否则,您应该联系 SMTP 开发人员以获取他们的 SMTP 主机名。
通过确保主机名正确来解决此问题。每个 SMTP 提供商都有自己的服务器设置。确保您使用的是正确的设置。如果您遇到任何错误,这将是您遇到的第一个问题。如果防火墙阻止网络,也可能导致邮件发送失败。
SmtpClient
的另一个问题是,如果您使用的端口号不正确,那么连接可能无法建立,最糟糕的是不会引发任何异常。例如,使用 295 端口号。命令将继续执行,没有任何成功消息或异常。确保您使用的是正确的端口号,否则请使用默认的 TCP 端口号;25。对我来说,25 端口号总是有效。
验证用户时出错
服务器需要正确的身份验证,因此您必须向服务器传递正确且必需的身份验证详细信息。第一阶段是启用连接上的 SSL。大多数情况下,如果连接不是通过 SSL 进行的,服务器会关闭连接。回顾本博客文章中的代码,并查看启用 SSL 命令,
client.EnableSsl = true;
在此之后,您应该确保您使用的用户名和密码组合是正确的。如果它们不正确,服务器可以自由地关闭连接。如果您的应用程序中发生此类(身份验证)问题,将引发以下异常。

服务器需要 SSL 连接,或者正确的用户名/密码组合。请确保您在这两种情况下都没有出错。
一旦这些问题解决(并且没有出现其他问题),您的电子邮件就会发送成功,您将在应用程序中看到成功消息,我的显示如下:

电子邮件发送成功!控制台应用程序中的成功消息。
关注点
在 .NET 框架中,您可以使用 System.Net
及其命名空间来处理网络。对于邮件,您可以使用 System.Net.Mail
命名空间。System.Net.Mail
公开了一个 SmtpClient
对象,该对象使用主机名和端口连接到 SMTP 服务器以发送电子邮件。有些服务器需要 SSL 连接和凭据(用户名/密码组合)。
MailMessage
是您用来发送电子邮件的对象,您可以用电子邮件消息的“发件人”、“收件人”、“主题”和“正文”字段填充此对象。SmtpClient
将发送此对象,您可以使用 Send
或 SendAsync
方法发送电子邮件,具体取决于您的框架和您将用来发送电子邮件的方法。
当代码到达 Send(或 SendAsync)函数时,SmtpClient
中会引发异常。这是因为连接问题发生在此阶段,服务器会告知 .NET 框架发送电子邮件时出现的错误,然后会引发异常。通常,异常是由于以下因素引起的,
- 用户名/密码不正确。
- 未启用 SSL。
- 主机名不正确,因此 SmtpClient 根本无法建立连接。
- 如果端口号不正确,则根本不会出现错误消息。这对每个开发人员来说都是一个棘手的部分。可以通过使用 25(默认 TCP 端口)来尽量减少这种情况。
SmtpClient 暴露了 Dispose()
函数,这就是为什么最好在 using 语句中使用 SmtpClient 对象,而不仅仅是作为一个简单的(普通)对象,稍后才调用 dispose。使用 using 语句可以让您将资源的释放留给 .NET 框架本身。
对于那些正在寻找 VB.NET 代码的人,您可以使用 Telerik 转换器将您的 C# 代码转换为 VB.NET 代码(反之亦然)。
.NET 允许您在运行于 .NET 上的不同框架和平台上使用相同的代码。例如 WPF、控制台应用程序、ASP.NET Web 应用程序。这就是为什么您几乎可以在所有应用程序中使用上述代码,无论是桌面应用程序、Web 应用程序还是任何您正在创建的客户端应用程序,只要它运行在 .NET 框架上。因为这些程序集存在于 .NET 中,而不是语言本身。