【问题标题】:libcurl C++: issue with printing responses when multiplexinglibcurl C++:多路复用时打印响应的问题
【发布时间】:2021-08-05 22:51:43
【问题描述】:

目标:

修改HTTP/2 multiplexinglibcurl 示例can be found here 以在到达某个缓冲区时保存有效负载响应,而不是像上述示例当前那样将其写入文件。然后缓冲区中的有效负载可用于打印、搜索字符串等操作。

预期输出:

每当callback 函数检测到一个已交付时,程序应将收到的每个有效负载响应打印到stdout

实际输出:

有时,对于少量传输,程序会按预期工作(请参阅下面main() 中的代码行int num_transfers = 3)。如果传输次数增加到 8 或 10,有时程序无法正常运行,程序仍会将输出打印到 stdout,但默认格式为 libcurl 如果没有 CURL_WRITEFUNCTION 将执行/CURL_WRITEDATA 已包含在代码中,可能表明callback 函数没有收到任何内容?同样在这种情况下,将打印错误数量的响应。

main() 内的主do...while 循环中,我将chunk.memorychunk.size 在打印出来后设置为0。如果不这样做,每次收到新的回复时,这些回复都会继续增长。不过,我不确定这是否是正确的方法。

当前尝试:

使用libcurl 示例that can be found here,我尝试模仿将输出写入回调函数的功能,如下所示(而不是将每个响应负载写入文件)。

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>
#include <curl/mprintf.h>

#ifndef CURLPIPE_MULTIPLEX
#define CURLPIPE_MULTIPLEX 0
#endif

struct CURLMsg *msg;

struct transfer {
    CURL *easy;
    unsigned int num;
    FILE *out;
};

struct MemoryStruct {
    char *memory;
    size_t size;
};

struct MemoryStruct chunk;

#define NUM_HANDLES 1000

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 = (char*)realloc(mem->memory, mem->size + realsize + 1);
    if(!ptr) {
        /* out of memory! */
        std::cout << "not enough memory (realloc returned NULL)" << std::endl;
        return 0;
    }

    mem->memory = ptr;
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;

    return realsize;
}

static void setup(struct transfer *t, int num)
{
    CURL *hnd;

    hnd = t->easy = curl_easy_init();

    curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, (void *)&chunk);

    /* set the same URL */
    curl_easy_setopt(hnd, CURLOPT_URL, "https://someurl.xyz");

    /* HTTP/2 please */
    curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);

    /* we use a self-signed test server, skip verification during debugging */
    curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);

    #if (CURLPIPE_MULTIPLEX > 0)
    /* wait for pipe connection to confirm */
    curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
    #endif
}

int main() {
    struct transfer trans[NUM_HANDLES];
    CURLM *multi_handle;
    int i;
    int still_running = 0; /* keep number of running handles */
    int num_transfers = 3;

    chunk.memory = (char*)malloc(1);
    chunk.size = 0;

    /* init a multi stack */
    multi_handle = curl_multi_init();

    for(i = 0; i < num_transfers; i++) {
        setup(&trans[i], i);

        /* add the individual transfer */
        curl_multi_add_handle(multi_handle, trans[i].easy);
    }

    curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);

    // Main loop
    do {
        CURLMcode mc = curl_multi_perform(multi_handle, &still_running);

        if(still_running) {
            /* wait for activity, timeout or "nothing" */
            mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
        }

        if(mc) {
            break;
        }

        // Get response
        do {
            int queued;
            msg = curl_multi_info_read(multi_handle, &queued);
            if ((msg) && (msg->msg == CURLMSG_DONE) && (msg->data.result == CURLE_OK)) {

                    // Print the response payload
                    std::cout << "size: " << chunk.size << std::endl;
                    std::cout << chunk.memory << std::endl;
                    chunk.memory = 0;
                    chunk.size = 0;

            }
            
        } while (msg);

    } while (still_running);

    for(i = 0; i < num_transfers; i++) {
        curl_multi_remove_handle(multi_handle, trans[i].easy);
        curl_easy_cleanup(trans[i].easy);
    }

    free(chunk.memory);
    curl_multi_cleanup(multi_handle);
        
    return 0;
}

总结性问题:

Q1.如何修改上述程序以正确地将接收到的有效负载响应异步保存到 structbuffer 中,以便它可以用于打印到 @ 等功能987654342@或搜索strings

【问题讨论】:

  • 您将相同的内存块传递给每个回调。您可能需要为每个简单的句柄创建一个单独的块。

标签: c++ libcurl http2 multiplexing


【解决方案1】:

首先,您应该将transfer 结构与访问输出的方式相关联:

struct transfer {
    CURL *easy;
    unsigned int num;
    std::string contents;
};

并将CURLOPT_WRITEDATA 与指针相关联:

curl_easy_setopt(hnd, CURLOPT_WRITEDATA, (void *)t);

然后,WriteMemoryCallback 变为:

static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    transfer *t = (transfer *)userp;
    size_t realsize = size * nmemb;    
    t->contents.append((const char *)contents, realsize);
    return realsize;
}

之后你可以在trans[i].contents变量中找到内容。

【讨论】:

  • 感谢您的帮助。我稍微修改了您的回调函数中的一行,将t-&gt;contents.append(contents, realsize); 更改为t-&gt;contents.append((const char*)contents, realsize);,因为从C++ 文档中可以看出append 不会接受void 类型的参数,尽管我不是100% 确定我的工作-大约。但是,在main() 中的do-while 循环中,这编译没有错误,使用` for (int i = 0; ...) { std::cout
  • 您对append 的更改是正确的,我将其包含在编辑中。不要忘记std::cout &lt;&lt; std::flushstd::cout &lt;&lt; std::endl 以确保输出显示在终端中,而不是卡在输出缓冲区中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-10
  • 1970-01-01
相关资源
最近更新 更多