我正在使用 libcurl 使用 GET 请求从网络服务器获取 json 数据。
这是我的示例代码:
char *DownloadedResponse;
static int writer(char *data, size_t size, size_t nmemb, char *buffer_in)
{
if (buffer_in != NULL)
{
buffer_in = new char[size*nmemb];
strcpy(buffer_in,data);
DownloadedResponse = buffer_in;
return size * nmemb;
}
return 0;
}
char * DownloadJSON(string URL)
{
CURL *curl;
CURLcode res;
struct curl_slist *headers=NULL;
curl_slist_append(headers, "Accept: application/json");
curl_slist_append( headers, "Content-Type: application/json");
curl_slist_append( headers, "charsets: utf-8");
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET,1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,writer);
res = curl_easy_perform(curl);
if (CURLE_OK == res)
{
char *ct;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
if((CURLE_OK == res) && ct)
{
cout<<"\nresponse received: "<<DownloadedResponse;
}
else
{
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl = NULL;
return NULL;
}
}
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl = NULL;
}
在这里我可以在
CURLOPT_WRITEFUNCTION
的回调“writer”中获取DownloadedResponse中的json数据。
但是如果我使用 CURLOPT_WRITEDATA 的自定义指针进行打印,
char *dataPointer = NULL;
CURLcode curl_easy_setopt(curl, CURLOPT_WRITEDATA, dataPointer);
cout<<dataPointer;
dataPointer 的输出为空。 这里有什么问题,因为我能够在
CURLOPT_WRITEFUNCTION
的回调中打印 json 数据,但不能在 CURLOPT_WRITEDATA
的指针中打印
您编写一个函数,获取从网络读取的数据,并将其写入您想要的位置。
static int writer(char *data, size_t size, size_t nmemb, char *buffer_in){
if (buffer_in != NULL) {
// very bad code which is never executed
}
return 0;
}
为了让该函数写入数据,它必须知道将其写入何处,因此您告诉它写入 NULL
char *dataPointer = NULL;
CURLcode curl_easy_setopt(curl, CURLOPT_WRITEDATA, dataPointer);
你告诉它使用什么值作为 buffer_in ?你向它传递 dataPointer,它是 NULL,所以你只需告诉它 buffer_in = NULL。我认为您的意思是说“dataPointer 的地址”,即 &dataPointer。
从技术上来说,我现在已经回答了你的问题。您为缓冲区传递了 NULL,因此写入函数立即退出。但还有更多。现在你可以在 writer() 中执行那些非常糟糕的代码。
if (buffer_in != NULL)
{
// if buffer_in already has allocated memory then leak it immediately
// create a new buffer of memory to leak later
buffer_in = new char[size*nmemb];
// store the data in buffer_in
// assume it is null terminated (it is not)
// rather than using the length we already know
strcpy(buffer_in,data);
// remember buffer_in? We don't use it so assign that data pointer to a global variable.
DownloadedResponse = buffer_in;
// return size of this particular chunk of data
return size * nmemb;
}
此函数必须使用数据的长度,并且不能假设数据以 null 结尾(请参阅 https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html)。
此函数必须能够通过将多个小块的数据添加到已读取的数据中来处理这些数据。你不能调用new然后丢弃新的内存。无论如何你都不能这样做,因为你刚刚泄漏了该内存——每个新的内存都必须与一个删除完全匹配。事实上,既然我们已经有了标准库,我们强烈建议您根本不要使用 new 或 delete。
这个函数应该使用你给它的 buffer_in 参数而不是全局变量,但是如果你愿意,你可以使用全局变量,这只是容易出错。这并不像其他东西那样是字面上的错误。
buffer_in 的全部意义在于为您提供一个持久的数据结构,您可以在其中累积答案。它可能应该在curl_easy_execute周围的本地范围内,所以如果你得到CURLE_OK,你就可以从该数据结构返回内容。我强烈建议您将数据写入 std::vector,这样您就不必跟踪内存分配。你有困难,但你根本不需要这样做。现代风格说每个人都会遇到麻烦,所以就让标准库来处理吧。
您声称遵循文档中的示例,该示例链接到 https://curl.haxx.se/libcurl/c/getinmemory.html如果您再看一次,您将看到他们在做什么,以及您的代码如何不匹配。特别是,它们传递 &chunk (块的地址),然后将数据写入块中,以便保留之前的内容。
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
// here is where they get access to the buffer
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
在调用curl时,会发现本地定义的struct,然后是远程调用:
struct MemoryStruct chunk;
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
res = curl_easy_perform(curl_handle);
if (stuff)
printf("%lu bytes retrieved\n", (long)chunk.size);