首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET SMTP 發送郵件三種方案完整對比:SmtpClient、CDO、Socket 底層實現

     編輯:ASP.NET 基礎

前言

.NET 發送郵件歷經三代演進:System.Web.Mail (1.x) → System.Net.Mail.SmtpClient (2.0+) → MailKit (跨平台現代)。本文詳解框架內置兩種高級用法(認證、SSL、異步)、CDO COM 組件方案、Socket 底層協議實現,並給出生產環境選型建議。

方案一:System.Net.Mail.SmtpClient (推薦標準)

1. 基礎配置 (web.config)

<system.net>
  <mailSettings>
    <smtp deliveryMethod="Network" from="noreply@example.com">
      <network host="smtp.example.com" port="587" 
               userName="noreply@example.com" password="secret" 
               enableSsl="true" />
    </smtp>
  </mailSettings>
</system.net>

2. 統一郵件發送服務

public interface IMailService
{
    Task SendAsync(string to, string subject, string body, bool isHtml = true);
    Task SendAsync(MailMessage message);
}

public class SmtpMailService : IMailService
{
    private readonly SmtpClient _client;
    
    public SmtpMailService()
    {
        _client = new SmtpClient(); // 自動讀取 web.config
    }
    
    public async Task SendAsync(string to, string subject, string body, bool isHtml = true)
    {
        using (var msg = new MailMessage
        {
            From = new MailAddress(_client.Host.Contains("localhost") ? "test@local" : "noreply@example.com"),
            Subject = subject,
            Body = body,
            IsBodyHtml = isHtml
        })
        {
            msg.To.Add(to);
            await _client.SendMailAsync(msg);
        }
    }
    
    public async Task SendAsync(MailMessage message) => await _client.SendMailAsync(message);
}

3. 進階功能:附件、嵌入圖片、異步回調

public async Task SendWithAttachmentAsync(string to, string subject, string body, 
    IEnumerable attachments, IEnumerable inlineImages = null)
{
    using (var msg = new MailMessage { Subject = subject, Body = body, IsBodyHtml = true })
    {
        msg.To.Add(to);
        foreach (var att in attachments) msg.Attachments.Add(att);
        if (inlineImages != null)
        {
            var view = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
            foreach (var img in inlineImages) view.LinkedResources.Add(img);
            msg.AlternateViews.Add(view);
        }
        await _client.SendMailAsync(msg);
    }
}

方案二:CDO (Collaboration Data Objects) - 僅限 .NET Framework Windows 環境

適用於需精細控制 SMTP 會話、NTLM 認證、舊系統遷移場景。

// 需引用 COM: "Microsoft CDO for Windows 2000 Library" (cdosys.dll)
public void SendViaCdo(string to, string subject, string htmlBody)
{
    var msg = new CDO.Message();
    msg.From = "sender@example.com";
    msg.To = to;
    msg.Subject = subject;
    msg.HTMLBody = htmlBody;
    
    var cfg = msg.Configuration;
    cfg.Fields["https://schemas.microsoft.com/cdo/configuration/sendusing"].Value = 2; // cdoSendUsingPort
    cfg.Fields["https://schemas.microsoft.com/cdo/configuration/smtpserver"].Value = "smtp.example.com";
    cfg.Fields["https://schemas.microsoft.com/cdo/configuration/smtpserverport"].Value = 587;
    cfg.Fields["https://schemas.microsoft.com/cdo/configuration/smtpauthenticate"].Value = 1; // cdoBasic
    cfg.Fields["https://schemas.microsoft.com/cdo/configuration/sendusername"].Value = "user";
    cfg.Fields["https://schemas.microsoft.com/cdo/configuration/sendpassword"].Value = "pwd";
    cfg.Fields["https://schemas.microsoft.com/cdo/configuration/smtpusessl"].Value = true;
    cfg.Fields.Update();
    
    msg.Send();
}

缺點:依賴 COM、非托管、難調試、不支持 .NET Core/跨平台。

方案三:Socket 手工實現 SMTP 協議 (教學/極致控制)


public class RawSmtpClient
{
    private readonly string _host;
    private readonly int _port;
    private readonly string _user;
    private readonly string _pass;
    
    public RawSmtpClient(string host, int port, string user, string pass)
    {
        _host = host; _port = port; _user = user; _pass = pass;
    }
    
    public void Send(string from, string to, string subject, string body)
    {
        using (var tcp = new TcpClient(_host, _port))
        using (var stream = tcp.GetStream())
        using (var reader = new StreamReader(stream, Encoding.ASCII))
        using (var writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true })
        {
            ReadResponse(reader); // 220
            
            WriteCommand(writer, $"EHLO {Dns.GetHostName()}");
            ReadResponse(reader); // 250
            
            // AUTH LOGIN
            WriteCommand(writer, "AUTH LOGIN");
            ReadResponse(reader); // 334 VXNlcm5hbWU6
            WriteCommand(writer, Convert.ToBase64String(Encoding.ASCII.GetBytes(_user)));
            ReadResponse(reader); // 334 UGFzc3dvcmQ6
            WriteCommand(writer, Convert.ToBase64String(Encoding.ASCII.GetBytes(_pass)));
            ReadResponse(reader); // 235
            
            WriteCommand(writer, $"MAIL FROM:<{from}>");
            ReadResponse(reader); // 250
            WriteCommand(writer, $"RCPT TO:<{to}>");
            ReadResponse(reader); // 250
            
            WriteCommand(writer, "DATA");
            ReadResponse(reader); // 354
            
            writer.WriteLine($"From: {from}");
            writer.WriteLine($"To: {to}");
            writer.WriteLine($"Subject: {subject}");
            writer.WriteLine("MIME-Version: 1.0");
            writer.WriteLine("Content-Type: text/html; charset=utf-8");
            writer.WriteLine();
            writer.WriteLine(body);
            writer.WriteLine(".");
            ReadResponse(reader); // 250
            
            WriteCommand(writer, "QUIT");
            ReadResponse(reader); // 221
        }
    }
    
    private void WriteCommand(StreamWriter w, string cmd) => w.WriteLine(cmd);
    private void ReadResponse(StreamReader r) 
    { 
        string line = r.ReadLine();
        if (!line.StartsWith("2") && !line.StartsWith("3")) 
            throw new Exception($"SMTP Error: {line}");
    }
}

三大方案對比選型

維度SmtpClientCDOSocket
易用性★★★★★★★☆☆☆★☆☆☆☆
功能完整度★★★★☆★★★★☆★★☆☆☆ (需自行擴展)
跨平台★★★☆☆ (.NET Core 支持)☆☆☆☆☆ (僅 Windows)★★★★★
性能高 (連接池)中 (COM 開銷)低 (無連接復用)
維護性最佳差 (COM 部署地獄)極差 (協議細節多)
適用場景95% 普通業務舊系統、特殊認證教學、嵌入式、無框架環境

生產環境最佳實踐

  1. 統一抽象:定義 IMailService,DI 注入 SmtpMailService,便於測試與替換。
  2. 異步發送:掛載到後台隊列,避免阻塞請求線程。
  3. 重試策略:Polly 實現指數退避重試,處理網絡抖動。
  4. 模板引擎:RazorLight / Scriban 生成豐富 HTML 郵件。
  5. 監控告警:記錄發送耗時、成功率、退信率,接入 Prometheus/Grafana。
  6. 新項目首選 MailKit:跨平台、支持 OAuth2、IMAP/POP3、更符合現代標準。

總結

常規業務直接用 SmtpClient + 配置文件 + 異步隊列即可滿足 99% 需求。CDO 僅作為遷移過渡方案。Socket 實現僅供協議學習。新架構請直接拥抱 MailKit,獲得最佳跨平台體驗與功能完整度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved