C++标准库无std::socket,必须用系统API或第三方库;手写SMTP易因TLS、编码、换行符、响应校验等问题失败,推荐libcurl等成熟封装。

为什么不能直接用 std::socket 写 SMTP 客户端
因为 C++ 标准库不提供网络通信原语,std::socket 根本不存在——这是常见误解。你得依赖操作系统 API(如 POSIX socket())或第三方库。手撸 TCP 连接 + SMTP 协议交互(HELO/EHLO、AUTH、MAIL FROM、RCPT TO、DATA)极易出错:TLS 握手失败、Base64 编码错误、换行符用 \r\n 而非 \n、响应码未校验都会导致邮件被拒或静默丢弃。
真实项目里,没人从零实现。你应该选成熟封装:
-
libcurl(最轻量,支持 STARTTLS 和 SSL/TLS,C 接口但 C++ 可直接调) -
POCO(C++ 原生,Poco::Net::SMTPClientSession封装完整,但二进制体积大) -
Boost.Beast(需自己拼 HTTP/SMTP,不推荐——SMTP 不是 HTTP)
用 libcurl 发送纯文本邮件的最小可行代码
前提是已安装 libcurl 开发包(Ubuntu: sudo apt-get install libcurl4-openssl-dev),编译时加 -lcurl。
关键点不是“怎么连”,而是“怎么构造符合 RFC 5321 的邮件体”:必须有 From:、To:、Subject:、空行分隔头与正文,且所有行以 \r\n 结尾:
立即学习“C++免费学习笔记(深入)”;
#include <curl/curl.h>
#include <string>
<p>size_t dummy_write(void<em>, size_t, size_t, void</em>) { return 0; }</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill4025" title="C++ 算法竞赛自动化测试数据生成与校验框架"><img
src="https://img.php.cn/upload/skill/000/000/081/178988956499722.jpg" alt="C++ 算法竞赛自动化测试数据生成与校验框架" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill4025" title="C++ 算法竞赛自动化测试数据生成与校验框架">C++ 算法竞赛自动化测试数据生成与校验框架</a>
<p>根据原题生成新题面、验证器及完整测试数据,自动套用 testlib 模板,用于用户要求生成测试数据时。</p>
</div>
<a href="/xiazai/skill4025" title="C++ 算法竞赛自动化测试数据生成与校验框架" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><p>int send_email() {
CURL *curl = curl_easy_init();
if (!curl) return -1;</p><p>std::string mail_data = "From: sender@example.com\r\n"
"To: receiver@example.com\r\n"
"Subject: Test from C++\r\n"
"\r\n"
"Hello, this is a test email.\r\n";</p><p>curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.example.com:587");
curl_easy_setopt(curl, CURLOPT_USERNAME, "sender@example.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "app-specific-password");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "<sender@example.com>");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, curl_slist_append(nullptr, "<receiver@example.com>"));
curl_easy_setopt(curl, CURLOPT_READFUNCTION, [](char <em>ptr, size_t size, size_t nmemb, void </em>userp) {
auto <em>data = static_cast<std::string</em>>(userp);
size_t len = std::min(size <em> nmemb, data->length());
memcpy(ptr, data->c_str(), len);
</em>data = data->substr(len);
return len;
});
curl_easy_setopt(curl, CURLOPT_READDATA, &mail_data);
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); // 强制 TLS
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 测试可关,生产必须开
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dummy_write); // 忽略响应体</p><p>CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
return res == CURLE_OK ? 0 : -1;
}</p>Google/Gmail 邮箱必须用应用专用密码,且禁用两步验证的“允许不够安全的应用”
直接用 Gmail 账户密码会失败,返回 535-5.7.8 Username and Password not accepted。原因:
- Gmail 已全面停用“允许不够安全的应用”开关
- 必须在 Google 账户设置中开启两步验证,再生成“应用专用密码”(16位字符)
- 该密码仅用于 SMTP 登录,和账户密码无关
- SMTP 服务器地址必须是
smtp.gmail.com,端口 587(STARTTLS)或 465(SSL)
若用 Outlook/Hotmail,对应地址是 smtp-mail.outlook.com,同样需应用密码。
附件和 HTML 邮件要用 MIME 多部分编码,别硬拼字符串
想加附件或 HTML 正文?手动构造 MIME 是灾难。例如一个带 PNG 附件的邮件,需:
- 生成唯一 boundary 字符串(如
boundary_abc123) - 设置
Content-Type: multipart/mixed; boundary="boundary_abc123" - 正文块用
--boundary_abc123\r\nContent-Type: text/html\r\n\r\n<h1>Hi</h1>\r\n - 附件块用
--boundary_abc123\r\nContent-Type: image/png; name="chart.png"\r\nContent-Transfer-Encoding: base64\r\n\r\niVBORw0KGgo...(Base64 编码后数据)\r\n - 结尾用
--boundary_abc123--\r\n
出错率极高。正确做法:用 libmime 或 Poco::Net::MailMessage 自动生成 MIME 结构,而不是自己 strcat。
真正卡住人的永远不是“怎么发”,而是“怎么让收件方邮箱客户端正确解析”。边界符少一个破折号、Base64 换行没对齐、HTML 中的引号没转义——都会导致附件丢失或正文变乱码。

















