libcurl CURLOPT_WRITEFUNCTION注意事项(转)

博客讨论了在使用libcurl库进行FTP下载时,如何处理接收到的数据。当缓冲区不足以存储所有数据时,应当检查剩余空间。如果小于CURL_MAX_WRITE_SIZE,则暂停接收,处理现有数据后再继续。示例代码展示了如何实现这一过程,包括使用curl_easy_pause函数暂停和恢复传输。

NAME

CURLOPT_WRITEFUNCTION - set callback for writing received data

SYNOPSIS

DESCRIPTION

Pass a pointer to your callback function, which should match the prototype shown above.

This callback function gets called by libcurl as soon as there is data received that needs to be saved. ptr points to the delivered data, and the size of that data is size multiplied with nmemb.

The callback function will be passed as much data as possible in all invokes, but you must not make any assumptions. It may be one byte, it may be thousands. The maximum amount of body data that will be passed to the write callback is defined in the curl.h header file: CURL_MAX_WRITE_SIZE (the usual default is 16K). IfCURLOPT_HEADER is enabled, which makes header data get passed to the write callback, you can get up toCURL_MAX_HTTP_HEADER bytes of header data passed into it. This usually means 100K.

This function may be called with zero bytes data if the transferred file is empty.

The data passed to this function will not be zero terminated!

Set the userdata argument with the CURLOPT_WRITEDATA option.

Your callback should return the number of bytes actually taken care of. If that amount differs from the amount passed to your callback function, it'll signal an error condition to the library. This will cause the transfer to get aborted and the libcurl function used will return CURLE_WRITE_ERROR.

If your callback function returns CURL_WRITEFUNC_PAUSE it will cause this transfer to become paused. Seecurl_easy_pause for further details.

Set this option to NULL to get the internal default function used instead of your callback. The internal default function will write the data to the FILE * given with CURLOPT_WRITEDATA.

遇到的坑:

申请了一块buffer用于接收FTP下载的数据,在缓存不足以接收传递到write_callback的数据大小时,只是拷贝了其中的一部分,并返回实际拷贝的数据长度,结果后续一直没有再接收数据。

ftp_ctx->current_download_len= 3181375488, recv_buffer_len=16384, end-last=4096, last=0x7f51c7d41010

其中,recv_buffer_len就是接收到的数据大小, end-last=4096就是剩余的缓存空间的大小。

对于这种情况,正确的处理是:在接收了一块数据之后,要对剩下的缓存进行判断,如果剩下的缓存小于CURL_MAX_WRITE_SIZE,那么暂停接收数据,转而去处理接收到的数据,处理完成腾出空间后再次接收。

    memcpy(ftp_ctx->pRecvBuf->last, buffer, recvlen);
    ftp_ctx->pRecvBuf->last += recvlen;
    ftp_ctx->current_down_len += recvlen;


    remain_len = ftp_ctx->pRecvBuf->end - ftp_ctx->pRecvBuf->last;
    if (remain_len < CURL_MAX_WRITE_SIZE)
    {
        curl_easy_pause(ftp_ctx->pDownCurl, CURLPAUSE_RECV);
        curl_easy_pause(ftp_ctx->pUploadCurl, CURLPAUSE_SEND_CONT);
    }


    return recvlen;

(完)

转自:https://blog.youkuaiyun.com/weiyuefei/article/details/51879095

<think> 我们正在查找关于CURLOPT_WRITEFUNCTION的使用实例。根据引用,我们知道CURLOPT_WRITEFUNCTION用于设置一个回调函数,该函数libcurl到数据时被调用。回调函数的原型为: size_t function(char *ptr, size_t size, size_t nmemb, void *userdata); 其中: - `ptr`:指向接到的数据的指针。 - `size`:总是1(根据引用[4]的描述,但实际中size表示每个数据块的大小,通常为1,而nmemb表示块数,所以总数据大小为size*nmemb)。 - `nmemb`:接到的数据块的数量。 - `userdata`:通过CURLOPT_WRITEDATA设置的用户数据。 回调函数应返回实际处理的数据字节数(通常为size*nmemb),如果返回的值与传入的大小不一致,libcurl会认为出现错误并中止传输。 引用[4]提供了一个例子,展示了如何将接到的数据累积到一个缓冲区中。我们也可以将数据写入文件或进行其他处理。 下面是一个使用实例的步骤: 1. 定义一个回调函数,用于处理接到的数据。 2. 使用`curl_easy_setopt`设置CURLOPT_WRITEFUNCTION,将这个回调函数传入。 3. 使用`curl_easy_setopt`设置CURLOPT_WRITEDATA,传入一个用户自定义数据的指针(例如,一个文件指针或一个结构体指针),这个指针将作为回调函数的userdata参数。 示例代码(C语言): ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <curl/curl.h> // 定义一个结构体来存储累积的数据 struct MemoryStruct { char *memory; size_t size; }; // 回调函数 static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; // 重新分配内存,包括额外的空字符空间 char *ptr = realloc(mem->memory, mem->size + realsize + 1); if(ptr == NULL) { // 内存分配失败 return 0; } mem->memory = ptr; memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; // 以空字符结尾,方便作为字符串使用 return realsize; } int main(void) { CURL *curl; CURLcode res; struct MemoryStruct chunk; chunk.memory = malloc(1); // 初始化为空字符串 chunk.size = 0; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if(curl) { // 设置URL curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); // 设置回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); // 传递我们的结构体指针给回调函数 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); // 执行请求 res = curl_easy_perform(curl); // 检查错误 if(res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } else { // 现在chunk.memory指向一个内存块,大小为chunk.size字节,并且以空字符结尾 printf("%lu bytes retrieved\n", (unsigned long)chunk.size); // 可以像使用字符串一样使用chunk.memory,但注意它可能包含二进制数据,所以如果需要处理二进制数据,不要依赖空字符结束。 // 这里我们打印前100个字符(或直到空字符) printf("%.*s\n", 100, chunk.memory); } // 清理 curl_easy_cleanup(curl); free(chunk.memory); } curl_global_cleanup(); return 0; } ``` 在这个例子中: - 我们定义了一个结构体`MemoryStruct`来保存累积的数据(一个动态分配的内存块和当前大小)。 - 回调函数`WriteMemoryCallback`将每次接到的数据追加到结构体的内存中,并更新大小。 - 在`main`函数中,我们初始化这个结构体,然后设置回调函数和用户数据(结构体指针)。 - 执行请求后,我们可以使用累积的数据(这里打印了前100个字节)。 - 最后记得释放分配的内存。 注意事项: - 回调函数必须返回实际处理的数据大小(即realsize),否则libcurl会认为有错误。 - 我们为字符串添加了空字符结尾,但这仅适用于文本数据。如果是二进制数据,则不需要空字符结尾,并且要注意数据中可能包含0。 - 在程序结束时,要释放动态分配的内存。 这个例子展示了如何将整个响应内容存储在内存中。如果你需要写入文件,可以使用文件指针作为userdata,并在回调函数中使用fwrite。 文件写入的简单回调函数示例: ```c static size_t write_file(void *ptr, size_t size, size_t nmemb, void *stream) { return fwrite(ptr, size, nmemb, (FILE *)stream); } ``` 然后在设置时: ```c FILE *fp = fopen("output.txt", "wb"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); ``` 最后别忘了关闭文件。 希望这个实例对你有帮助! </think>下面我将详细解释`CURLOPT_WRITEFUNCTION`的使用方法并提供完整示例代码。这个选项用于设置libcurl数据时的回调函数,是处理网络响应的核心机制。 ### 回调函数原型 ```c size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata); ``` 参数说明: - `ptr`:指向接数据的缓冲区指针 - `size`:数据块大小(通常为1) - `nmemb`:数据块数量(实际数据长度 = size * nmemb) - `userdata`:通过`CURLOPT_WRITEDATA`设置的自定义指针 ### 完整使用示例 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <curl/curl.h> // 定义存储响应的数据结构 struct ResponseData { char *data; size_t size; }; // 回调函数实现 size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) { size_t realsize = size * nmemb; struct ResponseData *res = (struct ResponseData *)userdata; // 扩展内存空间 char *new_ptr = realloc(res->data, res->size + realsize + 1); if (!new_ptr) return 0; // 内存分配失败 res->data = new_ptr; // 追加新数据 memcpy(&(res->data[res->size]), ptr, realsize); res->size += realsize; res->data[res->size] = '\0'; // 添加字符串终止符 return realsize; // 必须返回实际处理的数据量 } int main(void) { CURL *curl; CURLcode res; struct ResponseData response = {0}; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if(curl) { // 设置目标URL curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); // 设置回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); // 设置用户数据指针 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); // 执行请求 res = curl_easy_perform(curl); // 检查结果 if(res != CURLE_OK) { fprintf(stderr, "请求失败: %s\n", curl_easy_strerror(res)); } else { printf("接 %zu 字节数据:\n%.500s...\n", response.size, response.data); } // 清理资源 curl_easy_cleanup(curl); free(response.data); } curl_global_cleanup(); return 0; } ``` ### 关键点解析 1. **数据累积机制**: - 回调函数会被多次调用(每次接数据片段) - 使用`realloc`动态扩展内存空间 - 通过`memcpy`拼接待处理数据[^4] 2. **返回值要求**: - 必须返回实际处理的数据量(`size * nmemb`) - 返回小于传入值会导致传输中止(CURLE_WRITE_ERROR) 3. **内存管理**: - 初始化时:`struct ResponseData response = {0};` - 每次回调后添加终止符:`res->data[res->size] = '\0'` - 结束后必须释放内存:`free(response.data);` 4. **二进制数据处理**: - 对于非文本数据,移除添加终止符的操作 - 使用`size`字段记录实际数据长度 - 保存为二进制文件时使用`fwrite(ptr, 1, realsize, file)` ### 文件写入示例 ```c size_t write_file(void *ptr, size_t size, size_t nmemb, FILE *stream) { return fwrite(ptr, size, nmemb, stream); } // 使用方式 FILE *fp = fopen("output.jpg", "wb"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); ``` ### 常见问题解决 1. **数据不完整**: - 确保返回`size * nmemb`值 - 检查网络错误码`curl_easy_strerror(res)` 2. **内存泄漏**: - 每次`realloc`后检查返回值 - 结束时必须`free`分配的内存 3. **多线程问题**: - 为每个线程创建独立的`ResponseData` - 使用互斥锁保护共享资源[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值