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

C# 實作 POP3 郵件伺服器連線與郵件計數:Socket 程式設計實戰

     編輯:關於C語言

協議基礎

POP3 (Post Office Protocol v3) 埠號 110,純文字指令回應模式。核心流程:

  1. TCP 連線 → 伺服器回應 +OK
  2. USER username → +OK
  3. PASS password → +OK
  4. STAT → +OK msgCount totalBytes
  5. QUIT → 斷線

現代化實作

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class Pop3Client : IDisposable
{
    private readonly string _host;
    private readonly int _port;
    private readonly string _user;
    private readonly string _pass;
    private TcpClient _client;
    private NetworkStream _stream;
    private StreamReader _reader;
    private StreamWriter _writer;
    private bool _connected;

    public Pop3Client(string host, int port, string user, string pass)
    {
        _host = host;
        _port = port;
        _user = user;
        _pass = pass;
    }

    public async Task ConnectAsync()
    {
        _client = new TcpClient();
        await _client.ConnectAsync(_host, _port);
        _stream = _client.GetStream();
        _reader = new StreamReader(_stream, Encoding.ASCII);
        _writer = new StreamWriter(_stream, Encoding.ASCII) { AutoFlush = true };

        string welcome = await ReadResponseAsync();
        if (!welcome.StartsWith("+OK")) throw new Exception($"連線失敗: {welcome}");

        await SendCommandAsync($"USER {_user}");
        string userResp = await ReadResponseAsync();
        if (!userResp.StartsWith("+OK")) throw new Exception($"帳號錯誤: {userResp}");

        await SendCommandAsync($"PASS {_pass}");
        string passResp = await ReadResponseAsync();
        if (!passResp.StartsWith("+OK")) throw new Exception($"密碼錯誤: {passResp}");

        _connected = true;
    }

    public async Task GetMessageCountAsync()
    {
        EnsureConnected();
        await SendCommandAsync("STAT");
        string resp = await ReadResponseAsync();
        // +OK 3 3456
        if (!resp.StartsWith("+OK")) throw new Exception($"STAT 失敗: {resp}");
        var parts = resp.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        return int.Parse(parts[1]); // 郵件數
    }

    public async Task DisconnectAsync()
    {
        if (!_connected) return;
        await SendCommandAsync("QUIT");
        await ReadResponseAsync();
        Dispose();
    }

    private async Task SendCommandAsync(string cmd)
    {
        await _writer.WriteLineAsync(cmd);
    }

    private async Task ReadResponseAsync()
    {
        return await _reader.ReadLineAsync() ?? string.Empty;
    }

    private void EnsureConnected()
    {
        if (!_connected) throw new InvalidOperationException("未連線");
    }

    public void Dispose()
    {
        _reader?.Dispose();
        _writer?.Dispose();
        _stream?.Dispose();
        _client?.Dispose();
        _connected = false;
    }
}

// 使用範例
public static async Task Main()
{
    var pop = new Pop3Client("pop.gmail.com", 995, "user@gmail.com", "app-password"); // SSL 需另行處理
    try
    {
        await pop.ConnectAsync();
        int count = await pop.GetMessageCountAsync();
        Console.WriteLine($"新郵件數: {count}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"錯誤: {ex.Message}");
    }
    finally
    {
        await pop.DisconnectAsync();
    }
}

關鍵改進點 (vs 原始碼)

原始碼問題改進方案
同步阻塞 TcpClientasync/await 非阻塞,適配 UI / ASP.NET Core
硬編碼編碼 ASCII.GetBytesStreamReader/Writer(Encoding.ASCII) 統一管理
無錯誤碼解析,僅 InvalidOperationException依回應碼 +OK/-ERR 拋出具體異常
資源未釋放 (無 using/Dispose)實作 IDisposable,finally 確保釋放
單一職責違規 (連線+統計混在一起)分離 Connect, GetMessageCount, Disconnect
不支援 SSL/TLS (現代郵件伺服器強制)下節說明

SSL/TLS 支援 (必要)

現代 POP3 (Gmail, Outlook, Yahoo) 皆強制 **SSL (995) 或 STARTTLS (110)**。
.NET 核心庫無內建 SslStream 包裝 POP3,需自行升級:

// ConnectAsync 內部修改
_client = new TcpClient();
await _client.ConnectAsync(_host, _port);
_stream = _client.GetStream();

if (_port == 995) // Implicit SSL
{
    var ssl = new System.Net.Security.SslStream(_stream, false, (a,b,c,d) => true);
    await ssl.AuthenticateAsClientAsync(_host);
    _stream = ssl;
}
// 之後 _reader/_writer 包裝 _stream 即可

擴充功能建議

  • LIST / UIDL:取得郵件清單與唯一 ID。
  • RETR n:下載第 n 封完整郵件 (需 MIME 解析,建議用 MimeKit)。
  • DELE n:標記刪除 (需 QUIT 生效)。
  • TOP n lines:只取標頭 + 前 N 行內文。
  • 連線池 / 重試 / 逾時 / 取消 Token。

生產環境建議:直接用成熟庫

// NuGet: MailKit (含 POP3/IMAP/SMTP, 完整 SSL, MIME 解析, 非同步)
using MailKit.Net.Pop3;
using MailKit;

using (var client = new Pop3Client())
{
    await client.ConnectAsync("pop.gmail.com", 995, true); // true = SSL
    await client.AuthenticateAsync("user", "pass");
    int count = client.Count;
    // var msg = client.GetMessage(0); // MimeMessage 物件
    await client.DisconnectAsync(true);
}

手寫 Socket 適合學習協議、嵌入式環境或極度客製化需求;正式專案請使用 MailKit 或 OpenPop.NET,避免重造輪子並踩坑。

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