在 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 指出的那样,回调必须是静态的。