在 C++ 中使用 libcurl 发布,写回调参数是垃圾
Posting with libcurl in C++, write callback params are garbage
使用 Visual Studio 2013 社区在 Win64 上开发,使用跨平台 wxWidgets 部署到 Win64 和 Linux。我正在尝试使用 libcurl 通过 C++ 模拟以下 curl.exe 命令行:
curl.exe -X POST -g "single-url-string"
这是针对应用程序的 IoT 功能,其中最终用户提供单个 url 字符串来控制他们的设备。这个逻辑不只是作为外部进程执行 curl.exe 的原因是因为这个逻辑在它自己的线程中运行,而 wxWidgets 不支持在主线程之外启动外部可执行文件。
通常在使用 curl.exe 执行 POST 时,post 数据作为选项提供。这告诉 curl.exe 操作是对提供的 url 的 POST,这里是 POST 的数据。如您所见,我尝试做的是 GET 样式 url(参数嵌入 url),然后将操作更改为 POST。这样做是因为研究表明,要求最终用户提供两个单独的 url 和数据字符串对他们来说太复杂了。所以我们想出了这个最终用户必须提供的更简单的单一字符串,这通常只是从他们的设备手册中复制一个字符串,而不必解释该字符串,更不用说将它分解成单独的有意义的字符串了。
所以,手头的问题是:我有两个版本的简单 C++ libcurl POST 例程,但是在这两个版本中,写入回调接收到的参数都是错误的。这两个版本是 POST 和单个 url 字符串,以及 POST 和 post 数据作为 url 字符串的单独选项提供。
问题是 1) 使用单字符串版本不执行 POST,并且它的写入回调参数是错误的;和 2) 使用两个字符串版本确实执行 POST,但是写入回调参数是错误的,以不同的方式。
write回调中的数据指针参数在两个版本中都指向内存地址1,size参数在两个版本中看起来都不错,但是nmemb参数要么是一个巨大的随机值(单字符串版本),要么是零(两个字符串 POST 版本)。
这是我的代码,是的,我在应用启动时调用了 curl_global_init()。
size_t CX_IOT_THREAD::curl_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
// storage for transferred data:
const int dataStoreSize = CURL_MAX_WRITE_SIZE + 1;
char dataStore[dataStoreSize];
memset(dataStore, 0, dataStoreSize); // zeroed out
size_t dataSize = size * nmemb; // bytes sent
if (dataSize)
{
memcpy(dataStore, ptr, dataSize); // copy into buffer sized so we'll have a terminating NULL char
wxString msg = wxString::Format(wxT("%s"), dataStore); // send as event, eventually to the log
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg);
// must return byte count processed for libcurl to be happy:
return dataSize; /**/
}
return size; // should be dataSize, but because nmemb is bad, I’m using size; it works.
}
cx_int CX_IOT_THREAD::Post(std::string& url)
{
if (url.length() == 0)
return -1;
char errBuf[CURL_ERROR_SIZE];
errBuf[0] = '[=11=]';
static const char *postthis = "name=Bloke&age=67";
CURLcode ret;
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_URL, url.c_str());
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, postthis);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis));
curl_easy_setopt(hnd, CURLOPT_ERRORBUFFER, errBuf);
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, &CX_IOT_THREAD::curl_write_callback);
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.49.1");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
// curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
ret = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
if (ret != CURLE_OK)
{
wxString msg = wxString::Format(wxT("Attempted POST failed, libcurl return code '%d'."), (cx_int)ret);
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
cx_int len = strlen(errBuf);
if (len > 0)
msg = wxString::Format("%s%s", errBuf, ((errBuf[len - 1] != '\n') ? "\n" : ""));
else msg = wxString::Format("%s\n", curl_easy_strerror(ret));
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
}
return (cx_int)ret;
}
知道为什么写入回调参数不好吗?知道为什么单字符串版本甚至不执行 post 吗? (单字符串版本是上面的 2 个 POSTFIELDS 选项被注释掉并启用了 CUSTOMREQUEST 选项。)
正如 Igor Tandetnik 指出的那样,回调必须是静态的。
使用 Visual Studio 2013 社区在 Win64 上开发,使用跨平台 wxWidgets 部署到 Win64 和 Linux。我正在尝试使用 libcurl 通过 C++ 模拟以下 curl.exe 命令行:
curl.exe -X POST -g "single-url-string"
这是针对应用程序的 IoT 功能,其中最终用户提供单个 url 字符串来控制他们的设备。这个逻辑不只是作为外部进程执行 curl.exe 的原因是因为这个逻辑在它自己的线程中运行,而 wxWidgets 不支持在主线程之外启动外部可执行文件。
通常在使用 curl.exe 执行 POST 时,post 数据作为选项提供。这告诉 curl.exe 操作是对提供的 url 的 POST,这里是 POST 的数据。如您所见,我尝试做的是 GET 样式 url(参数嵌入 url),然后将操作更改为 POST。这样做是因为研究表明,要求最终用户提供两个单独的 url 和数据字符串对他们来说太复杂了。所以我们想出了这个最终用户必须提供的更简单的单一字符串,这通常只是从他们的设备手册中复制一个字符串,而不必解释该字符串,更不用说将它分解成单独的有意义的字符串了。
所以,手头的问题是:我有两个版本的简单 C++ libcurl POST 例程,但是在这两个版本中,写入回调接收到的参数都是错误的。这两个版本是 POST 和单个 url 字符串,以及 POST 和 post 数据作为 url 字符串的单独选项提供。
问题是 1) 使用单字符串版本不执行 POST,并且它的写入回调参数是错误的;和 2) 使用两个字符串版本确实执行 POST,但是写入回调参数是错误的,以不同的方式。
write回调中的数据指针参数在两个版本中都指向内存地址1,size参数在两个版本中看起来都不错,但是nmemb参数要么是一个巨大的随机值(单字符串版本),要么是零(两个字符串 POST 版本)。
这是我的代码,是的,我在应用启动时调用了 curl_global_init()。
size_t CX_IOT_THREAD::curl_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
// storage for transferred data:
const int dataStoreSize = CURL_MAX_WRITE_SIZE + 1;
char dataStore[dataStoreSize];
memset(dataStore, 0, dataStoreSize); // zeroed out
size_t dataSize = size * nmemb; // bytes sent
if (dataSize)
{
memcpy(dataStore, ptr, dataSize); // copy into buffer sized so we'll have a terminating NULL char
wxString msg = wxString::Format(wxT("%s"), dataStore); // send as event, eventually to the log
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg);
// must return byte count processed for libcurl to be happy:
return dataSize; /**/
}
return size; // should be dataSize, but because nmemb is bad, I’m using size; it works.
}
cx_int CX_IOT_THREAD::Post(std::string& url)
{
if (url.length() == 0)
return -1;
char errBuf[CURL_ERROR_SIZE];
errBuf[0] = '[=11=]';
static const char *postthis = "name=Bloke&age=67";
CURLcode ret;
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_URL, url.c_str());
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, postthis);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis));
curl_easy_setopt(hnd, CURLOPT_ERRORBUFFER, errBuf);
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, &CX_IOT_THREAD::curl_write_callback);
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.49.1");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
// curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
ret = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
if (ret != CURLE_OK)
{
wxString msg = wxString::Format(wxT("Attempted POST failed, libcurl return code '%d'."), (cx_int)ret);
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
cx_int len = strlen(errBuf);
if (len > 0)
msg = wxString::Format("%s%s", errBuf, ((errBuf[len - 1] != '\n') ? "\n" : ""));
else msg = wxString::Format("%s\n", curl_easy_strerror(ret));
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
}
return (cx_int)ret;
}
知道为什么写入回调参数不好吗?知道为什么单字符串版本甚至不执行 post 吗? (单字符串版本是上面的 2 个 POSTFIELDS 选项被注释掉并启用了 CUSTOMREQUEST 选项。)
正如 Igor Tandetnik 指出的那样,回调必须是静态的。