12

我在 C++ 中使用libcurl ,并且我使用Boost.Thread从我的 UI调用curl_easy_perform一个单独的线程。

主 UI 有一个取消按钮,我希望它能够完美响应(即,当用户单击它时,它应该立即做出反应)。我已经设置了读取、写入和进度回调来读取原子should_cancel变量(如在这个问题中),但是有两个问题:

  1. 从按下取消到卷曲操作完成时,通常会有一个非常小的(但很明显)的延迟。

  2. 有时,会有很长的(有时是无休止的)延迟。在这种情况下,要么:

    一种。很长时间没有调用进度、读取和写入回调,或者

    湾。调用进度回调,我返回一个非零值(意味着它应该终止),但是 curl 操作有一段时间没有完成(实际上,同时再次调用了进度函数!)

所以:

  1. 为什么会发生长时间的延迟(尤其是在没有调用进度函数的情况下)?
  2. 我应该怎么做才能让取消按钮正确反应?

一种可能性是告诉 UI 取消操作成功,但继续在后台运行 curl 线程,直到它取消。这个问题(我认为)是它强制should_cancel变量是全局的,而不是限定在操作开始的对话框中。

4

3 回答 3

15

我在 curl 7.21.6 上遇到了同样的问题。当试图中止 smtp 协议时。从读取回调返回 CURL_READFUNC_ABORT 会停止传输,但 curl_easy_perform 在接下来的 5 分钟内不会返回。可能它等待 tcp 超时。

为了四处走动,我存储了 curl 使用的套接字(替换 curl_opensocket_callback),并在需要时直接关闭此套接字。

curl_socket_t storeCurlSocket(SOCKET *data, curlsocktype purpose, struct curl_sockaddr *addr) {
    SOCKET sock = socket(addr->family, addr->socktype, addr->protocol);
    *data = sock;
    return sock;
}

size_t abort_payload(void *ptr, size_t size, size_t nmemb, SOCKET *curl_socket) {
    SOCKET l_socket = INVALID_SOCKET;
    swap(l_socket, *curl_socket);
    if (l_socket != INVALID_SOCKET) {
        shutdown(l_socket, SD_BOTH);
        closesocket(l_socket);
    }
    return CURL_READFUNC_ABORT;
}

...calling perform...
        SOCKET curlSocket;
        curet = curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, storeCurlSocket);
        curet = curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &curlSocket);
        curet = curl_easy_setopt(curl, CURLOPT_READFUNCTION, abort_payload);
        curet = curl_easy_setopt(curl, CURLOPT_READDATA, &curlSocket);
        curet = curl_easy_perform(curl);
        if (curet == CURLE_ABORTED_BY_CALLBACK) {
          //expected abort
        }
...
于 2011-08-24T10:32:20.013 回答
4

你的基本想法是对的。您应该从 UI 中分离 curl 操作。但是,应该对实现进行一些更改。您不应该使用全局should_cancel. 相反,您应该有一个current_request指向类型对象的全局指针Request。这种类型应该有一个内部cancel标志和一个公共Cancel()函数。作为对取消按钮的响应,您调用Cancelcurrent_request然后将其设为空。然后,取消的请求将负责稍后自己的清理(毕竟它是一个线程)。

您需要小心使用互斥锁以防止僵尸对象。取消和请求完成之间存在固有的竞争条件。

于 2010-12-16T10:06:50.800 回答
1

延迟可能是因为curl_easy_perform忙于写入或读取回调,尝试从写入回调或CURL_READFUNC_ABORT读取回调返回 0。根据文档,如果写回调返回的值与函数接收到的值不同,传输将被中止,如果读回调返回CURL_READFUNC_ABORT,传输也将被中止(从库的版本 7.12.1 开始有效)。

于 2010-12-16T00:34:53.193 回答