You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++使用libcurl发送SMTP邮件无报错但目标邮箱收不到邮件

libcurl SMTP发信失败排查与修复

针对你疑问的明确答复

  • 你在main里定义的const char *data = payload_text本身是合法的内存指针,但你没有把它传给CURLOPT_READDATA(你实际传的是全局的payload_text),且就算传入,因为没有配套的读取位置跟踪逻辑,也无法正常工作。
  • 你实现的read_function不符合libcurl的回调规范:
    • 回调的第四个用户数据参数应为void*类型,你写的char*存在类型兼容风险
    • 没有维护读取偏移量,每次被调用都从payload起始位置拷贝数据,永远不会返回0(libcurl约定返回0代表数据传输完成,即EOF),会导致libcurl无限重复读取内容
    • 没有判断缓冲区最大可写入长度size * nmemb,直接拷贝全量payload会造成内存越界写入

其他导致发信失败的致命代码错误

  • CURLOPT_MAIL_FROM参数格式错误:该参数要求传入包裹在尖括号里的完整发件邮箱地址,例如<youraccount@gmail.com>,你传入的普通字符串"my mail"会导致SMTP的MAIL FROM命令执行失败
  • CURLOPT_MAIL_RCPT参数类型错误:该参数要求传入curl_slist*类型的收件人链表,不能直接传入字符串,你传裸值会导致libcurl无法正确生成SMTP的RCPT TO命令,服务端识别不到收件人
  • 邮件payload里的To、From头填的是占位符mymail/mailto,不是真实邮箱地址,就算请求发出去也会被邮件服务商直接拦截丢弃
  • 资源释放逻辑错误:仅在请求失败时调用curl_easy_cleanup清理句柄,请求成功时会造成资源泄漏;如果curl初始化失败,直接调用curl_easy_perform会触发空指针崩溃

修复后的可运行参考代码

#include <iostream>
#include <curl/curl.h>
#include <cstring>

// 替换为真实的发件人、收件人邮箱
static const char *payload_text =
  "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n"
  "To: <target@example.com>\r\n"
  "From: <youraccount@gmail.com>\r\n"
  "Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@rfcpedant.example.org>\r\n"
  "Subject: SMTP example message\r\n"
  "\r\n" /* 空行分隔邮件头和正文,符合RFC5322要求 */
  "The body of the message starts here.\r\n"
  "\r\n"
  "It could be a lot of lines, could be MIME encoded, whatever.\r\n"
  "Check RFC5322.\r\n";

// 跟踪数据读取进度的结构
struct ReadStatus {
    const char *data_ptr;
    size_t read_pos;
    size_t total_len;
};

size_t read_function(char *buffer, size_t size, size_t nmemb, void *userp)
{
    size_t max_write_len = size * nmemb;
    if (max_write_len == 0) return 0;

    ReadStatus *status = static_cast<ReadStatus*>(userp);
    size_t remain_len = status->total_len - status->read_pos;
    if (remain_len == 0) return 0; // 数据全部读完,返回0通知libcurl传输结束

    size_t copy_len = remain_len > max_write_len ? max_write_len : remain_len;
    memcpy(buffer, status->data_ptr + status->read_pos, copy_len);
    status->read_pos += copy_len;
    return copy_len;
}

int main()
{
    CURL *curl;
    CURLcode res = CURLE_OK;
 
    curl = curl_easy_init();
    if(curl) 
    {
        ReadStatus read_status;
        read_status.data_ptr = payload_text;
        read_status.read_pos = 0;
        read_status.total_len = strlen(payload_text);

        // 构造收件人链表
        struct curl_slist *recipients = NULL;
        recipients = curl_slist_append(recipients, "<target@example.com>");

        curl_easy_setopt(curl, CURLOPT_USERNAME, "youraccount@gmail.com"); 
        // Gmail需使用开启两步验证后生成的16位应用专用密码,不能填账号登录密码
        curl_easy_setopt(curl, CURLOPT_PASSWORD, "your_app_specific_password");
        curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
        curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "<youraccount@gmail.com>");
        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
        curl_easy_setopt(curl, CURLOPT_READDATA, &read_status);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_function);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);

        res = curl_easy_perform(curl);
        
        if(res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }

        // 无论请求成功失败,都要释放所有分配的资源
        curl_slist_free_all(recipients);
        curl_easy_cleanup(curl);
    }
    return 0;
}

后续排查建议

  • 你已经开启了CURLOPT_VERBOSE,运行时直接看终端打印的SMTP交互日志,确认MAIL FROM、RCPT TO、DATA命令返回的都是2xx开头的成功状态码,如果返回5xx错误码根据提示调整参数即可
  • 如果日志显示邮件已经提交到SMTP服务器但收件箱没找到,先检查垃圾邮件文件夹,没有配置SPF/DKIM签名的测试邮件大概率会被归类为垃圾邮件
  • 如果登录阶段报错,确认邮箱已开启SMTP服务,Gmail账号必须开启两步验证后生成应用专用密码使用,不支持直接用账号密码登录SMTP

内容的提问来源于stack exchange,提问作者Franco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 04:54:12