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

C++ Libcurl写访问违例问题:多URL并行下载代码报错修复

解决Libcurl多URL下载时的write access violation错误

错误根源

你的代码触发写访问违规的核心原因是:

  • 在add_transfer函数中,CURLOPT_WRITEDATA被错误赋值为&write_cb(写回调函数的地址),而非用于存储下载数据的std::string对象地址。当write_cb尝试将该地址强制转换为std::string*并调用append时,实际操作的是函数指针所在的非法内存区域,直接引发内存访问错误。
  • 额外问题:重复调用curl_multi_perform、传输完成后未清理句柄、未继续添加剩余URL任务。

修改后的完整代码

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <string>
#include <vector>

static const char* urls[] = {
  "http://www.example.com",
  "http://www.example1.com",
};

#define MAX_PARALLEL 10 /* 同时传输的数量 */
#define NUM_URLS sizeof(urls)/sizeof(char *)

// 写回调:将下载的数据追加到std::string中
static size_t write_cb(void* ptr, size_t size, size_t nmemb, void* buffer)
{
    std::string* str = static_cast<std::string*>(buffer);
    str->append(static_cast<char*>(ptr), size * nmemb);
    return size * nmemb; // 返回实际处理的字节数,原代码只返回nmemb是错误的
}

// 添加一个新的传输任务
static void add_transfer(CURLM* cm, const char* url, std::string* data_buf, int* left)
{
    CURL* eh = curl_easy_init();
    if (!eh) return;

    curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, write_cb);
    curl_easy_setopt(eh, CURLOPT_URL, url);
    // 将数据缓冲区地址传给WRITEDATA,供回调使用
    curl_easy_setopt(eh, CURLOPT_WRITEDATA, data_buf);
    curl_easy_setopt(eh, CURLOPT_VERBOSE, 1L);
    // 将数据缓冲区地址存在PRIVATE中,方便传输完成后访问
    curl_easy_setopt(eh, CURLOPT_PRIVATE, data_buf);

    curl_multi_add_handle(cm, eh);
    (*left)++;
}

int main(void)
{
    CURLM* cm;
    unsigned int next_url = 0;
    int left = 0;
    // 为每个URL分配独立的缓冲区存储下载数据
    std::vector<std::string> download_buffers(NUM_URLS);

    curl_global_init(CURL_GLOBAL_ALL);
    cm = curl_multi_init();

    // 设置最大同时连接数
    curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)MAX_PARALLEL);

    // 初始化第一批传输任务
    for (; next_url < MAX_PARALLEL && next_url < NUM_URLS; next_url++) {
        add_transfer(cm, urls[next_url], &download_buffers[next_url], &left);
    }

    do {
        int still_alive = 1;
        CURLMcode mc = curl_multi_perform(cm, &still_alive);

        if (mc != CURLM_OK) {
            fprintf(stderr, "curl_multi_perform failed: %s\n", curl_multi_strerror(mc));
            break;
        }

        // 等待活动、超时或无操作
        mc = curl_multi_poll(cm, NULL, 0, 1000, NULL);
        if (mc != CURLM_OK) {
            fprintf(stderr, "curl_multi_poll failed: %s\n", curl_multi_strerror(mc));
            break;
        }

        // 处理完成的传输
        CURLMsg* msg;
        int queued;
        while ((msg = curl_multi_info_read(cm, &queued))) {
            if (msg->msg == CURLMSG_DONE) {
                CURL* eh = msg->easy_handle;
                std::string* data_buf = nullptr;
                // 获取传输对应的缓冲区
                curl_easy_getinfo(eh, CURLINFO_PRIVATE, &data_buf);

                fprintf(stderr, "Transfer completed for URL: ");
                const char* url;
                curl_easy_getinfo(eh, CURLINFO_EFFECTIVE_URL, &url);
                fprintf(stderr, "%s\n", url);
                // 可以在这里使用data_buf中的下载数据,比如打印长度
                if (data_buf) {
                    fprintf(stderr, "Downloaded data length: %zu bytes\n", data_buf->size());
                }

                // 清理完成的传输句柄
                curl_multi_remove_handle(cm, eh);
                curl_easy_cleanup(eh);
                left--;

                // 如果还有未处理的URL,添加新的传输任务
                if (next_url < NUM_URLS) {
                    add_transfer(cm, urls[next_url], &download_buffers[next_url], &left);
                    next_url++;
                }
            }
        }

    } while (left > 0);

    curl_multi_cleanup(cm);
    curl_global_cleanup();

    return EXIT_SUCCESS;
}

关键修改点说明

  1. 修复WRITEDATA赋值:将CURLOPT_WRITEDATA设置为实际存储数据的std::string对象地址,确保回调函数能正确访问合法内存。
  2. 独立数据缓冲区:为每个URL分配单独的std::string,避免多传输任务下的数据竞争,同时保证每个任务的数据独立存储。
  3. 修正回调返回值:原代码只返回nmemb,正确的返回值应该是size * nmemb,否则Libcurl会认为数据未被正确接收,可能中断传输。
  4. 完善传输完成逻辑:传输结束后移除并清理CURL句柄,同时自动添加剩余的URL任务,直到所有URL处理完成。
  5. 移除重复调用:删除了原代码中重复的curl_multi_perform调用,避免不必要的重复处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 20:35:14