- 后端
- 通信
【免费下载链接】MailKit
A cross-platform .NET library for IMAP, POP3, and SMTP.
MailKit 是一个跨平台的 .NET 邮件客户端库,基于 MimeKit 构建,为 IMAP、POP3、SMTP 三种协议提供了完整的客户端实现(源码见 MailKit/MailKit.csproj,项目说明见 README.md)。本文以官方 NuGet 入门指南 nuget/GettingStarted.md 为骨架,完整演示从安装到发送邮件、收取邮件、以及使用 IMAP 的高级能力(摘要抓取、按需下载 MIME 部件、标记/删除、搜索排序、文件夹导航)的实战路径,并结合仓库源码解释连接参数与底层实现,读完即可将 MailKit 集成到自己的 .NET 应用中。
一、安装 MailKit
通过 NuGet 安装是最快的方式(详见 README.md):
Install-Package MailKit在项目文件中也可以直接添加 PackageReference:
<PackageReference Include="MailKit" Version="*" />MailKit 使用SmtpClient、Pop3Client、ImapClient三个客户端类分别封装三种协议,它们都位于MailKit.Net.*命名空间下。解析与构造邮件对象则依赖 MimeKit(MimeMessage、TextPart等),因此写代码时通常需要同时引入以下命名空间:
using MailKit; using MailKit.Net.Smtp; // 或 MailKit.Net.Pop3 / MailKit.Net.Imap using MimeKit;二、使用 SMTP 发送邮件
发送邮件是 MailKit 最常用的场景之一。官方入门示例 nuget/GettingStarted.md 给出了一个最小可运行的控制台程序:
using System; using MailKit.Net.Smtp; using MailKit; using MimeKit; namespace TestClient { class Program { public static void Main (string[] args) { var message = new MimeMessage (); message.From.Add (new MailboxAddress ("Joey Tribbiani", "joey@friends.com")); message.To.Add (new MailboxAddress ("Mrs. Chanandler Bong", "chandler@friends.com")); message.Subject = "How you doin'?"; message.Body = new TextPart ("plain") { Text = @"Hey Chandler, I just wanted to let you know that Monica and I were going to go play some paintball, you in? -- Joey" }; using (var client = new SmtpClient ()) { client.Connect ("smtp.friends.com", 587, false); // Note: only needed if the SMTP server requires authentication client.Authenticate ("joey", "password"); client.Send (message); client.Disconnect (true); } } } }整个流程可拆解为四步:
- 构造
MimeMessage:通过From/To添加地址(MailboxAddress的第一个参数是显示名,第二个是邮箱地址),设置Subject,用TextPart作为纯文本正文。需要发送带附件、HTML 正文的邮件时,可改用BodyBuilder,参考 Documentation/Examples/BodyBuilder.cs。 - 连接 SMTP 服务器:
Connect (host, port, useSsl)的第三个布尔参数表示是否直接使用 SSL/TLS 加密连接(对应枚举SecureSocketOptions.SslOnConnect)。 - 认证(可选):只有服务器要求认证时才需要调用
Authenticate。MailKit 支持 CRAM-MD5、DIGEST-MD5、LOGIN、NTLM、PLAIN、SCRAM-SHA-1/256/512 以及 OAUTHBEARER、XOAUTH2 等 SASL 机制(见 README.md 与 MailKit/Security/ 目录下的SaslMechanism*实现)。 - 发送并断开:
Send完成后用Disconnect (true)退出,true表示发送 QUIT 命令优雅关闭连接。
更推荐的连接方式:SecureSocketOptions
入门示例中的Connect (host, port, bool useSsl)是历史遗留的简化重载。从源码看,SmtpClient真正完整的方法是(见 MailKit/Net/Smtp/SmtpClient.cs):
public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default)SecureSocketOptions枚举(定义于 MailKit/Security/SecureSocketOptions.cs)提供了更精细的加密控制:
| 选项 | 行为 |
|---|---|
None | 完全不使用 SSL/TLS 加密 |
Auto | 默认值,由 MailKit 自动决定;若服务器不支持加密则继续明文连接 |
SslOnConnect | 连接建立后立即升级为 SSL/TLS(即传统 SMTPS 直连,端口 465) |
StartTls | 收到服务器问候与能力列表后强制 STARTTLS,服务器不支持则抛出NotSupportedException |
StartTlsWhenAvailable | 服务器支持 STARTTLS 才升级,否则继续明文连接 |
源码注释还说明了Auto模式的端口推断规则:端口为 465 时默认使用SslOnConnect,其他端口默认使用StartTlsWhenAvailable。因此对现代服务器,更稳健的写法是:
await client.ConnectAsync ("smtp.friends.com", 587, SecureSocketOptions.StartTls);MailKit 的所有 API 都提供了对应的*Async异步版本,并且支持传入CancellationToken取消操作,在服务器无响应时不会永久阻塞(见 README.md)。推荐在 ASP.NET Core、桌面 UI 等场景下优先使用异步 API。
三、通过 POP3 收取邮件
POP3 客户端的使用同样简单。入门文档给出了完整示例:
using System; using MailKit.Net.Pop3; using MailKit; using MimeKit; namespace TestClient { class Program { public static void Main (string[] args) { using (var client = new Pop3Client ()) { client.Connect ("pop.friends.com", 110, false); client.Authenticate ("joey", "password"); for (int i = 0; i < client.Count; i++) { var message = client.GetMessage (i); Console.WriteLine ("Subject: {0}", message.Subject); } client.Disconnect (true); } } } }关键点说明:
client.Count返回服务器上邮件的总数,GetMessage (index)按序号(从 0 开始)下载完整邮件,返回解析好的MimeMessage对象,可直接读取Subject、Body等属性。- 与 IMAP 不同,POP3 协议设计上是"下载即删"式的离线协议:
GetMessage会把整封邮件全部下载下来,无法像 IMAP 那样只抓取摘要或单个 MIME 部件。 - 除 SASL 机制外,POP3 客户端还支持 APOP 和
USER/PASS认证,并支持 TOP、UIDL、STLS、UTF8 等扩展(见 README.md)。 - 连接参数同样支持
SecureSocketOptions,加密收件可写为client.Connect ("pop.friends.com", 995, SecureSocketOptions.SslOnConnect)。
四、使用 IMAP:读取收件箱
MailKit 官方认为 IMAP 支持比 POP3 更重要(nuget/GettingStarted.md)。IMAP 是"在线"协议,邮件保留在服务器上,客户端可以按需读取。基础读取示例:
using System; using MailKit.Net.Imap; using MailKit.Search; using MailKit; using MimeKit; namespace TestClient { class Program { public static void Main (string[] args) { using (var client = new ImapClient ()) { client.Connect ("imap.friends.com", 993, true); client.Authenticate ("joey", "password"); // The Inbox folder is always available on all IMAP servers... var inbox = client.Inbox; inbox.Open (FolderAccess.ReadOnly); Console.WriteLine ("Total messages: {0}", inbox.Count); Console.WriteLine ("Recent messages: {0}", inbox.Recent); for (int i = 0; i < inbox.Count; i++) { var message = inbox.GetMessage (i); Console.WriteLine ("Subject: {0}", message.Subject); } client.Disconnect (true); } } } }要点:
client.Inbox是所有 IMAP 服务器都保证存在的收件箱文件夹;Open (FolderAccess.ReadOnly)以只读方式打开,避免误改服务器状态。若需增删改,应使用FolderAccess.ReadWrite。inbox.Count与inbox.Recent分别对应EXISTS与RECENT响应。- IMAP 客户端支持非常广泛的扩展,包括 IDLE(实时推送)、CONDSTORE/QRESYNC(增量同步)、SORT/THREAD、SPECIAL-USE、MOVE、METADATA、QUOTA 等(完整清单见 README.md),这也是 IMAP 客户端实现远比 POP3 复杂的原因。
4.1 只抓取邮件摘要:Fetch
IMAP 相对 POP3 的核心优势是:可以在不下载整封邮件的情况下,只获取邮件的摘要信息。使用Fetch系列方法,可以为文件夹中任意范围的消息请求任意子集的摘要:
foreach (var summary in inbox.Fetch (0, -1, MessageSummaryItems.Full)) { Console.WriteLine ("[summary] {0:D2}: {1}", summary.Index, summary.Envelope.Subject); }Fetch (start, end, items)中0, -1表示从第 0 条到最后一封(包含全部消息)。MessageSummaryItems.Full请求完整的信封信息;也可以按需组合位标志,例如MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure。对应的底层 FETCH 指令组装与解析实现在 MailKit/Net/Imap/ImapFolderFetch.cs 中,单元测试见 UnitTests/Net/Imap/ImapFolderFetchTests.cs。
4.2 按需下载单个 MIME 部件:GetBodyPart
摘要 + 正文结构(BodyStructure)的组合还有一个杀手级用途:只下载邮件中的某一个部件,而不是整封邮件。例如只取text/plain正文、只取text/html正文,甚至只取某个图片附件:
foreach (var summary in inbox.Fetch (0, -1, MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure)) { if (summary.TextBody != null) { // this will download *just* the text/plain part var text = inbox.GetBodyPart (summary.UniqueId, summary.TextBody); } if (summary.HtmlBody != null) { // this will download *just* the text/html part var html = inbox.GetBodyPart (summary.UniqueId, summary.HtmlBody); } // if you'd rather grab, say, an image attachment... it might look something like this: if (summary.Body is BodyPartMultipart) { var multipart = (BodyPartMultipart) summary.Body; var attachment = multipart.BodyParts.OfType<BodyPartBasic> ().FirstOrDefault (x => x.FileName == "logo.jpg"); if (attachment != null) { // this will download *just* the attachment var part = inbox.GetBodyPart (summary.UniqueId, attachment); } } }summary.TextBody/summary.HtmlBody是BodyPart引用,GetBodyPart只向服务器请求该部件对应的 BODY 段。多部分正文通过BodyPartMultipart.BodyParts遍历,可用 LINQ 按FileName定位附件。更完整的用法可参考 Documentation/Examples/ImapBodyPartExamples.cs。
4.3 设置消息标志(Flags)
将邮件标记为"已读"(\Seen)是典型的标志操作。要设置标志,只需要消息的 UID(或序号)以及它所属的文件夹:
folder.Store (uid, new StoreFlagsRequest (StoreAction.Add, MessageFlags.Seen) { Silent = true });StoreAction.Add表示"追加标志",其他值见StoreAction枚举(MailKit/StoreAction.cs),如Remove清除标志、Replace整体替换。MessageFlags.Seen即 IMAP 的\Seen;枚举中还包含Answered、Deleted、Flagged、Draft等标准标志。Silent = true表示不要求服务器返回更新后的标志(减少网络往返);相关请求类型定义见 MailKit/StoreFlagsRequest.cs。
4.4 删除邮件
IMAP 删除邮件是两步操作:先标记\Deleted,再可选地执行 EXPUNGE 真正清除:
folder.Store (uid, new StoreFlagsRequest (StoreAction.Add, MessageFlags.Deleted) { Silent = true }); folder.Expunge ();标记\Deleted的写法与标记\Seen完全一致。注意Expunge会不可恢复地删除所有被标记的消息;如果只想删除指定消息,可结合 UID 范围调用Expunge的重载。FolderAccess.ReadOnly模式下无法执行上述存储/删除操作。
4.5 搜索与排序
MailKit 提供了类型安全的查询构建器SearchQuery,无需手写 IMAP 搜索字符串。下面的示例搜索"2013-01-12 之后送达、主题包含 MailKit 且已读"的邮件:
// let's search for all messages received after Jan 12, 2013 with "MailKit" in the subject... var query = SearchQuery.DeliveredAfter (DateTime.Parse ("2013-01-12")) .And (SearchQuery.SubjectContains ("MailKit")).And (SearchQuery.Seen); foreach (var uid in inbox.Search (query)) { var message = inbox.GetMessage (uid); Console.WriteLine ("[match] {0}: {1}", uid, message.Subject); } // let's do the same search, but this time sort them in reverse arrival order var orderBy = new [] { OrderBy.ReverseArrival }; foreach (var uid in inbox.Sort (query, orderBy)) { var message = inbox.GetMessage (uid); Console.WriteLine ("[match] {0}: {1}", uid, message.Subject); } // you'll notice that the orderBy argument is an array... this is because you // can actually sort the search results based on multiple columns: orderBy = new [] { OrderBy.ReverseArrival, OrderBy.Subject }; foreach (var uid in inbox.Sort (query, orderBy)) { var message = inbox.GetMessage (uid); Console.WriteLine ("[match] {0}: {1}", uid, message.Subject); }SearchQuery的完整查询谓词(日期、标志、主题、正文、头部、UID、注解等)见 MailKit/Search/SearchQuery.cs,对应实现与测试在 UnitTests/Net/Imap/ImapFolderSearchTests.cs 与 UnitTests/Search/SearchQueryTests.cs。inbox.Sort (query, orderBy)依赖服务器支持 SORT/ESORT 扩展(README.md);orderBy是数组,支持按多列排序(如先按到达时间倒序、再按主题)。- 拿到 UID 后,除了
GetMessage下载整封邮件,也可以配合前面的Fetch只取摘要信息,或做标志、删除等后续操作。
4.6 文件夹导航
MailKit 可以列出个人命名空间下的顶层文件夹:
// Get the first personal namespace and list the toplevel folders under it. var personal = client.GetFolder (client.PersonalNamespaces[0]); foreach (var folder in personal.GetSubfolders (false)) Console.WriteLine ("[folder] {0}", folder.Name);如果服务器支持 SPECIAL-USE 或 XLIST(Gmail)扩展,可以直接拿到预定义的 All、Drafts、Flagged(即 Important)、Junk、Sent、Trash 等文件夹:
if ((client.Capabilities & (ImapCapabilities.SpecialUse | ImapCapabilities.XList)) != 0) { var drafts = client.GetFolder (SpecialFolder.Drafts); } else { // maybe check the user's preferences for the Drafts folder? }当服务器不支持上述扩展时,就需要自行设计启发式规则来定位 Sent/Drafts/Trash 文件夹。入门文档给出了两种等价写法——循环匹配:
static string[] CommonSentFolderNames = { "Sent Items", "Sent Mail", "Sent Messages", /* maybe add some translated names */ }; static IFolder GetSentFolder (ImapClient client, CancellationToken cancellationToken) { var personal = client.GetFolder (client.PersonalNamespaces[0]); foreach (var folder in personal.GetSubfolders (false, cancellationToken)) { foreach (var name in CommonSentFolderNames) { if (folder.Name == name) return folder; } } return null; }以及用 LINQ 简化的版本:
static string[] CommonSentFolderNames = { "Sent Items", "Sent Mail", "Sent Messages", /* maybe add some translated names */ }; static IFolder GetSentFolder (ImapClient client, CancellationToken cancellationToken) { var personal = client.GetFolder (client.PersonalNamespaces[0]); return personal.GetSubfolders (false, cancellationToken).FirstOrDefault (x => CommonSentFolderNames.Contains (x.Name)); }另一种常见做法是允许用户在应用设置中自行指定 Sent/Drafts/Trash 等文件夹,具体策略取决于产品需求。命名空间枚举(client.PersonalNamespaces)在连接后由 NAMESPACE 扩展填充(MailKit/Net/Imap/ImapClient.cs)。
五、更多示例与参考资料
- 代码示例集:Documentation/Examples/ 目录收录了大量可运行片段,包括 SmtpExamples.cs、Pop3Examples.cs、ImapExamples.cs、ImapBodyPartExamples.cs、BodyBuilder.cs、DkimExamples.cs、FilterExamples.cs 等。
- 完整示例应用:samples/ 下包含
ImapClientDemo(桌面版)、ImapClientDemo.Android、ImapClientDemo.iOS以及 samples/ImapIdle/(展示 IDLE 实时监控新邮件)等多个可编译工程。 - 常见问题:FAQ.md 整理了高频问题,其中包含如何开启协议日志(ProtocolLog)辅助排查。
- 版本变更:ReleaseNotes.md 记录了各版本的功能演进与破坏性变更,升级前建议查阅。
六、排障建议
官方入门指南 nuget/GettingStarted.md 在"Reporting Bugs"一节给出了两条非常实用的排障经验,同样适用于日常开发:
- 与邮件服务器交互异常时,务必提供协议日志。MailKit 的
ProtocolLogger(见 MailKit/ProtocolLogger.cs)可以记录客户端与服务器之间的原始命令/响应,例如:using (var client = new SmtpClient (new ProtocolLogger ("smtp.log"))) { // ... }没有协议日志,服务器行为几乎无法定位。更完整的说明见 FAQ.md 的 ProtocolLog 章节。
- 报告异常时不要只贴
Exception.Message,还要附上Exception.StackTrace——仅凭 Message 往往无法判断错误发生在协议层、TLS 层还是 I/O 层。MailKit 抛出的SmtpCommandException、ImapCommandException、SmtpProtocolException、ImapProtocolException等异常类型定义在 MailKit/Net/ 各协议目录中,StackTrace 能帮你区分是哪一类。
通过本文的完整示例,你已经可以用 MailKit 完成"发信(SMTP)→ 收信(POP3/IMAP)→ 摘要抓取 → 按需下载 → 标记与删除 → 搜索排序 → 文件夹导航"的全链路开发。所有代码均可直接复制到 .NET 项目中运行,官方示例与源码测试(UnitTests/)提供了进一步的参考价值。
- 后端
- 通信
【免费下载链接】MailKit
A cross-platform .NET library for IMAP, POP3, and SMTP.
相关推荐
MailKit 跨平台邮件客户端库完全指南:基于 .NET 的 SMTP、POP3 与 IMAP 开发实战
MailKit 跨平台邮件客户端库完全指南:基于 .NET 的 SMTP、POP3 与 IMAP 开发实战 MailKit 是构建于 MimeKit https
后端通信深入解析MailKit:掌握IMAP、POP3和SMTP邮件协议的终极指南
深入解析MailKit:掌握IMAP、POP3和SMTP邮件协议的终极指南 MailKit是一个功能强大的跨平台.NET邮件客户端库,专门用于处理IMAP、PO
后端通信MailKit 遥测指标完全指南:Socket 与 SMTP/POP3/IMAP 客户端 OpenTelemetry 指标详解
MailKit 遥测指标完全指南:Socket 与 SMTP/POP3/IMAP 客户端 OpenTelemetry 指标详解 本指南以 MailKit 官方
后端通信
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考