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

郵件群發系統實戰:核心模組代碼解析與架構設計

     編輯:關於C語言

系統需求與架構概覽

  • 核心功能:多執行緒異步發送、發送狀態標記、資料逐條提取、氣泡提示、最小化到系統托盤、即時進度條。
  • 技術棧:C# .NET Framework, System.Net.Mail, System.Threading, OleDb (Access 資料庫), WinForms。 架構分層:UI 層 (Form) → 控制層 (發送協調器) → 資料存取層 (郵件隊列/收件人清單) → 通訊層 (SMTP Client 封裝)。

關鍵命名空間

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Threading;
using System.Text;
using System.Windows.Forms;
using System.Net.Mail;
using System.Net;
using System.Text.RegularExpressions;

核心類別設計

1. 郵件實體類

public class MailEntity
{
    public int Id { get; set; }
    public string ToAddress { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
    public bool IsHtml { get; set; }
    public List Attachments { get; set; }
    public MailStatus Status { get; set; } // Pending, Sending, Sent, Failed
    public string ErrorMessage { get; set; }
    public DateTime? SentTime { get; set; }
}

2. SMTP 設定類

public class SmtpConfig
{
    public string Host { get; set; }
    public int Port { get; set; } = 25;
    public string UserName { get; set; }
    public string Password { get; set; }
    public bool EnableSsl { get; set; }
    public int TimeoutMs { get; set; } = 30000;
}

3. 發送引擎 (核心邏輯)


public class MailSender : IDisposable
{
    private readonly SmtpConfig _config;
    private readonly SmtpClient _client;
    private readonly Queue _queue;
    private readonly object _lock = new object();
    private volatile bool _running;
    private Thread _workerThread;

    public event Action OnMailStatusChanged;
    public event Action OnProgressChanged; // current, total

    public MailSender(SmtpConfig config, IEnumerable mails)
    {
        _config = config;
        _client = new SmtpClient(config.Host, config.Port)
        {
            Credentials = new NetworkCredential(config.UserName, config.Password),
            EnableSsl = config.EnableSsl,
            Timeout = config.TimeoutMs
        };
        _queue = new Queue(mails);
    }

    public void Start()
    {
        if (_running) return;
        _running = true;
        _workerThread = new Thread(SendLoop) { IsBackground = true };
        _workerThread.Start();
    }

    public void Stop()
    {
        _running = false;
        _workerThread?.Join();
    }

    private void SendLoop()
    {
        int total = _queue.Count;
        int sent = 0;

        while (_running && _queue.Count > 0)
        {
            MailEntity mail;
            lock (_lock) { if (_queue.Count == 0) break; mail = _queue.Dequeue(); }

            mail.Status = MailStatus.Sending;
            OnMailStatusChanged?.Invoke(mail);

            try
            {
                using (var msg = new MailMessage())
                {
                    msg.From = new MailAddress(_config.UserName);
                    msg.To.Add(mail.ToAddress);
                    msg.Subject = mail.Subject;
                    msg.Body = mail.Body;
                    msg.IsBodyHtml = mail.IsHtml;
                    foreach (var att in mail.Attachments ?? Enumerable.Empty())
                        msg.Attachments.Add(new Attachment(att));

                    _client.Send(msg); // 同步發送,阻塞執行緒
                }
                mail.Status = MailStatus.Sent;
                mail.SentTime = DateTime.Now;
            }
            catch (Exception ex)
            {
                mail.Status = MailStatus.Failed;
                mail.ErrorMessage = ex.Message;
            }

            OnMailStatusChanged?.Invoke(mail);
            OnProgressChanged?.Invoke(++sent, total);
        }
    }

    public void Dispose() => _client?.Dispose();
}

4. UI 協調器 (Form 代碼片段)


public partial class Form1 : Form
{
    private MailSender _sender;
    private BindingList _mailList;
    private NotifyIcon _trayIcon;

    public Form1()
    {
        InitializeComponent();
        InitTrayIcon();
        LoadMailsFromDb();
        dataGridView1.DataSource = _mailList;
    }

    private void LoadMailsFromDb()
    {
        _mailList = new BindingList();
        using (var conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=mails.mdb"))
        using (var cmd = new OleDbCommand("SELECT * FROM Mails WHERE Status=0", conn))
        {
            conn.Open();
            using (var reader = cmd.ExecuteReader())
                while (reader.Read())
                    _mailList.Add(new MailEntity {
                        Id = reader.GetInt32(0),
                        ToAddress = reader.GetString(1),
                        Subject = reader.GetString(2),
                        Body = reader.GetString(3),
                        IsHtml = reader.GetBoolean(4),
                        Status = MailStatus.Pending
                    });
        }
    }

    private void btnStart_Click(object sender, EventArgs e)
    {
        var config = new SmtpConfig { Host = txtHost.Text, Port = int.Parse(txtPort.Text), 
            UserName = txtUser.Text, Password = txtPwd.Text, EnableSsl = chkSsl.Checked };
        _sender = new MailSender(config, _mailList);
        _sender.OnMailStatusChanged += mail => this.Invoke((Action)(() => dataGridView1.Refresh()));
        _sender.OnProgressChanged += (cur, tot) => this.Invoke((Action)(() => {
            progressBar1.Value = cur * 100 / tot;
            lblProgress.Text = $"{cur}/{tot}";
        }));
        _sender.Start();
        btnStart.Enabled = false;
        btnStop.Enabled = true;
    }

    private void btnStop_Click(object sender, EventArgs e) => _sender?.Stop();

    private void Form1_Resize(object sender, EventArgs e)
    {
        if (WindowState == FormWindowState.Minimized) { Hide(); _trayIcon.Visible = true; }
    }

    private void InitTrayIcon()
    {
        _trayIcon = new NotifyIcon { Icon = Icon, Text = "郵件群發中...", Visible = false };
        _trayIcon.DoubleClick += (s, e) => { Show(); WindowState = FormWindowState.Normal; _trayIcon.Visible = false; };
    }
}

關鍵技術點總結

需求實現方案關鍵類/方法
異步發送不凍結 UI專用背景執行緒 + Control.Invoke 回報進度Thread, IsBackground=true, Invoke
逐條狀態追蹤實體類含 Status 枚舉,事件驅動 UI 更新MailEntity, BindingList, OnMailStatusChanged
資料持久化Access + OleDb,狀態欄位標記避免重發OleDbConnection, OleDbCommand
托盤常駐NotifyIcon,縮小時隱藏主視窗NotifyIcon, Form.Resize
即時進度條事件傳遞 (current, total),UI 線程計算百分比ProgressBar, OnProgressChanged
氣泡提示NotifyIcon.ShowBalloonTip 於狀態變更/完成時觸發NotifyIcon
郵件格式驗證Regex 驗證 Email 格式,發送前預檢System.Text.RegularExpressions

擴展建議

  • 併發控制:引入 SemaphoreSlim 限制同時 SMTP 連線數,避免被封 IP。
  • 重試機制:失敗郵件加入重試隊列,指數退避策略。
  • 模板引擎:整合 Razor/RazorLight 實現郵件模板化。
  • 日誌審計:Serilog/NLog 記錄發送詳情,支援查詢與統計。
  • 資料庫升級:遷移至 SQLite/SQL Server,支援更大數據量與並發。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved