【问题标题】:libcurl delays for 1 second before uploading data, command-line curl does notlibcurl 在上传数据前延迟 1 秒,命令行 curl 不会
【发布时间】:2013-06-29 17:52:38
【问题描述】:

我正在使用 libcurl 将 API 命令发送到本地服务(即在 127.0.0.1 上)。

该程序旨在替换 shell 脚本(使用 curl 程序。)

一切正常,除了某处有 1 秒延迟,即从我调用 curl_easy_perform() 到第一次调用我的读取回调函数时经过了 1 秒。

C 程序正在使用这些选项(省略错误检查和回调代码):

curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:12345/x");
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
curl_easy_setopt(curl, CURLOPT_INFILESIZE, (long)getLengthOfCommandObject());
curl_easy_setopt(curl, CURLOPT_READFUNCTION, &myReadFunction);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &myWriteFunction);

但如果我像这样从 shell 运行curl

$ curl --data-binary '<command>' http://127.0.0.1:12345/x

它立即发送请求,没有 1 秒的延迟。

什么可能导致延迟,我可以设置一个选项来防止它吗?


编辑服务器基于mongoose

【问题讨论】:

  • 你试过用 strace 执行你的二进制文件吗?它可能会显示程序执行的任何延迟。 strace 的手册页有很多时间相关的选项。
  • @VoidPointer,strace 没有透露任何相关信息,但我找到了原因。看我的回答。
  • 即使是 libcurl 的作者think this behaviour of libcurl is a mistake.

标签: c curl libcurl mongoose-web-server


【解决方案1】:

延迟的原因是:

客户端的一个解决方案是像这样禁用Expect 标头:

headers = curl_slist_append(NULL, "Expect:");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// ...
result = curl_easy_perform(curl);
curl_slist_free_all(headers);

Equivalent fix for PHP clientrelated PHP question

【讨论】:

  • 只是出于兴趣,您如何检查 libcurl 是否正在发送 Expect: 100-continue 标头?我也有类似的延迟,也许这就是问题所在……我只是不确定如何检查这是否是在我的请求标头中发送的。谢谢
【解决方案2】:

此答案不是答案,而是在curl 中显示问题出在哪里。 libcurl 与基于 mongooseCivet 有同样的问题。

我遇到了同样的问题并运行strace 来尝试调试。这是strace 的输出(每行都有一个以秒为单位的时间戳)。问题是在发送标头之后,在发送正文之前,有一系列 poll( ...events=POLLIN...) 调用阻塞了总共 1000 毫秒 (1+98+1+900)。您可以看到从 17.935024 到 18.931842 的时间戳差距。

17.934881 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 5
17.934898 fcntl(5, F_GETFL)                       = 0x2 (flags O_RDWR)
17.934916 fcntl(5, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
17.934934 connect(5, {sa_family=AF_INET, sin_port=htons(37034), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
17.934952 poll([{fd=5, events=POLLOUT|POLLWRNORM}], 1, 0) = 1 ([{fd=5, revents=POLLOUT|POLLWRNORM}])
17.934970 getsockopt(5, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
17.934988 getpeername(5, {sa_family=AF_INET, sin_port=htons(37034), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0
17.935006 getsockname(5, {sa_family=AF_INET, sin_port=htons(46830), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0
17.935024 sendto(5, "POST /foo/1/bar HTTP/"..., 168, MSG_NOSIGNAL, NULL, 0) = 168
17.935042  poll([{fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
17.935060  poll([{fd=5, events=POLLIN}], 1, 1)     = 0 (Timeout)
17.935078  poll([{fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
18.029421  poll([{fd=5, events=POLLIN}], 1, 98)    = 0 (Timeout)
18.029518  poll([{fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
18.030576  poll([{fd=5, events=POLLIN}], 1, 1)     = 0 (Timeout)
18.030681  poll([{fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
18.931571  poll([{fd=5, events=POLLIN}], 1, 900)   = 0 (Timeout)
18.931642  poll([{fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
18.931698  poll([{fd=5, events=POLLIN}, {fd=5, events=POLLOUT}], 2, 1000) = 1 ([{fd=5, revents=POLLOUT}])
18.931726  poll([{fd=5, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}, {fd=5, events=POLLOUT|POLLWRNORM}], 2, 0) = 1 ([{fd=5, revents=POLLOUT|POLLWRNORM}])
18.931842 sendto(5, "{\"foo\":[{\"bar\":\"A\",\"e"..., 1642, MSG_NOSIGNAL, NULL, 0) = 1642

我在使用命令行curlcurl_easy_perform 时都遇到了同样的问题,但使用 Postman 等其他 REST 客户端时我没有遇到问题。

【讨论】:

    【解决方案3】:

    感谢@finnw 的回答和此处的讨论 (https://groups.google.com/g/mongoose-users/c/92fD1Elk5m4?pli=1),我能够通过以下方式修补 CivetWeb 以支持 100-continue 功能(请参阅 #ifdef DEXPECT_100_CONTINUE_FIX)。

    void
    CivetServer::getPostData(struct mg_connection *conn, std::string& dst)
    {
    
        struct mg_request_info *ri = mg_get_request_info(conn);
        assert(ri != NULL);
        CivetServer *me = (CivetServer*) (ri->user_data);
        assert(me != NULL);
        mg_lock_context(me->context);
        CivetConnection &conobj = me->connections[conn];
        mg_lock_connection(conn);
        mg_unlock_context(me->context);
    
    #ifdef EXPECT_100_CONTINUE_FIX
        const char * expect_str = mg_get_header(conn, "Expect");
        if (expect_str)
        {
            printf("CivetServer::getParam, Expect:%s\n", expect_str);
            if (strcmp(expect_str,"100-continue") == 0)
            {
                mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
            }
        }
        else
        {
            printf("CivetServer::getParam, Expect: blank\n");
        }
    #endif
    

    【讨论】:

      猜你喜欢
      • 2017-05-20
      • 1970-01-01
      • 2013-02-28
      • 2018-09-12
      • 1970-01-01
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 2015-03-25
      相关资源
      最近更新 更多