EmailSender 函数手册
C++ 后端服务器框架EmailSender 函数手册
CHAPTER 18

EmailSender 函数手册

5 分钟阅读 · 1,271 字

依据源码:include/EmailSender.h、src/EmailSender.cpp。


0. 模块定位与总体结构

EmailSender 是一个轻量 SMTP 客户端封装,支持:

  • 单例获取
  • SMTP 参数初始化
  • 单收件人 / 多收件人发送
  • AUTH LOGIN 认证
  • 纯文本邮件正文发送

关键状态成员

  • smtpHost_, smtpPort_, username_, password_
  • lastError_
  • std::mutex mutex_
  • static EmailSender* instance_

1. 单例与初始化

1.1 static EmailSender* getInstance()

  • 位置:EmailSender.cpp
  • 实现:
    1. static EmailSender inst;
    2. instance_ = &inst;
    3. 返回 instance_

1.2 void init(const std::string& smtpHost, unsigned short smtpPort, const std::string& username, const std::string& password)

  • 作用:设置 SMTP 连接参数并清空上次错误。
  • 并发:函数内部 lock_guard<mutex_>,确保参数更新与发送互斥。
  • 行为:
    • 覆盖旧配置
    • lastError_.clear()

2. 编码辅助

2.1 static std::string base64Encode(const std::string& raw)

  • 作用:SMTP AUTH LOGIN 需要对用户名/密码做 Base64 编码。
  • 实现要点:
    • 3 字节分组编码
    • 尾部不足 3 字节时补 =
    • 返回标准 Base64(不是 URL 安全变体)

3. 网络连接与协议收发

3.1 bool smtpConnect(int* sockOut)

  • 作用:建立到 SMTP 服务器的 TCP 连接。

步骤

  1. Windows 下 WSAStartup(每次调用都初始化一次)
  2. getaddrinfo(smtpHost_, smtpPort_)
  3. socket(...)
  4. connect(...)
  5. 成功后 *sockOut = sock

失败路径

  • 每个失败分支都设置 lastError_
  • Windows 下在失败时调用 WSACleanup()
  • 释放 addrinfo、关闭 socket(如已创建)

注意点

  • hints.ai_family = AF_INET:当前只走 IPv4
  • 无连接超时参数(依赖系统默认)

3.2 bool smtpCommandRecv(int sock, std::string* outLine)

  • 作用:从 socket 读取一行 SMTP 响应(简化实现)。

逻辑

  1. 循环 recv 到本地 line
  2. 若 n <= 0 返回 false
  3. 终止条件:
    • line.size() >= 4 && line[3] == ' '(SMTP 最终行)
    • 或已包含 \r\n
  4. 截断到首个 \r\n 之前
  5. 若 outLine 非空则写入

边界

  • 对多行响应处理较简化,主要适配当前使用的命令流。

3.3 bool smtpCommand(int sock, const std::string& cmd, int expectCode)

  • 作用:发送 SMTP 命令并校验响应码。

步骤

  1. 发送 cmd + "\r\n"
  2. 调用 smtpCommandRecv 收响应
  3. 解析前 3 位数字为 code
  4. return code == expectCode

注意

  • 若 send 或 recv 失败,直接返回 false
  • 不自动写 lastError_(多数错误信息由调用方设置)

4. 发送主流程

4.1 bool sendMailImpl(const std::string& from, const std::vector<std::string>& toList, const std::string& subject, const std::string& body)

  • 作用:完整 SMTP 会话(核心函数)。

0) 前置检查

  • 若 smtpHost_ 或 username_ 为空:
    • lastError_ = "EmailSender not initialized. Call init() first."
    • 返回 false

1) 建连与服务器问候

  1. smtpConnect(&sock) 失败返回 false
  2. 首次 smtpCommandRecv,要求响应 220
  3. 否则:
    • lastError_ = "SMTP greeting failed: " + line
    • 关闭 socket,返回 false

2) 标识与认证

  1. 先 EHLO localhost 期待 250
  2. 失败则回退 HELO localhost 期待 250
  3. AUTH LOGIN 期待 334
  4. 发送 base64(username_) 期待 334
  5. 发送 base64(password_) 期待 235

任一步失败都设置 lastError_ 并关闭连接返回 false。

3) 邮件信封

  1. MAIL FROM:<from> 期待 250
  2. 遍历 toList:
    • 去掉空白字符
    • 空字符串跳过
    • RCPT TO:<recipient> 期待 250
  3. 若任何收件人失败:
    • lastError_ = "RCPT TO failed: ..."
    • 关闭连接返回 false

4) 邮件正文

  1. DATA 期待 354
  2. 构造数据:
    • From: ...
    • To: ...(用逗号拼接)
    • Subject: ...
    • Content-Type: text/plain; charset=UTF-8
    • 空行
    • 正文 body
    • 终止符 \r\n.
  3. 实际发送字符串为 data + "\r\n"(即 SMTP data block 的 <CRLF>.<CRLF>)
  4. 读响应,要求 250

失败则设置 lastError_ 并返回 false。

5) 结束会话

  1. smtpCommand(sock, "QUIT", 221)(返回值未强制检查)
  2. 关闭 socket
  3. Windows 下 WSACleanup()
  4. 返回 true

5. 对外发送接口

5.1 bool sendMail(const std::string& from, const std::string& to, const std::string& subject, const std::string& body)

  • 作用:字符串收件人版本(支持逗号或分号分隔)。
  • 逻辑:
    1. 解析 to:
      • 分隔符 , 或 ;
      • 忽略空白
    2. 形成 toList
    3. 调用向量版本 sendMail(from, toList, subject, body)

5.2 bool sendMail(const std::string& from, const std::vector<std::string>& toList, const std::string& subject, const std::string& body)

  • 作用:线程安全入口。
  • 并发控制:
    • lock_guard<mutex_>
    • 保护配置读写与 lastError_
  • 实际执行:
    • 调用 sendMailImpl(...)

6. 只读接口

std::string getLastError() const

  • 定义在头文件内联
  • 返回 lastError_ 的副本

说明:该接口本身未加锁;但主写路径在 sendMail 和 init 中已加锁。若极端并发下要求严格一致性,可考虑读也加锁或使用原子/线程安全日志通道。


7. 跨平台实现差异

Windows

  • 使用 Winsock:
    • WSAStartup
    • closesocket
    • WSACleanup
  • _MSC_VER 下链接 ws2_32.lib

Linux/Unix

  • POSIX socket:
    • socket/connect/send/recv/close

8. 语义与边界说明(面试高频)

  1. 协议支持范围
    • 当前是明文 SMTP + AUTH LOGIN,未实现 STARTTLS/SMTPS。
  2. 超时控制
    • 代码中无 socket 超时设置,网络异常可能阻塞较久。
  3. 邮件格式
    • 仅文本正文,未做 MIME multipart、附件、HTML。
  4. 响应解析
    • 多行响应处理简化,适配常见服务器但并非完整 RFC 严格实现。
  5. 资源清理
    • 成功路径与失败路径基本都有 socket 关闭;Windows 清理在多个分支执行。

9. 改进建议(可直接面试回答)

  • 增加 TLS(STARTTLS / SMTPS 465)
  • 增加 socket 超时与重试策略
  • 完整 SMTP 多行响应解析
  • MIME/附件/HTML 支持
  • 引入异步发送队列与失败重投
  • 抽象错误码,而非仅 lastError_ 文本

下一模块(顺序)

JsonObjectView → docs/modules/detail/JsonObjectView_FUNCTIONS.md

全部章节