如何立即取消卷曲操作?

时间:2010-12-15 23:51:07

标签: c++ multithreading curl boost-thread

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

主UI有一个取消按钮,我想要完全响应(即,当用户点击它时,它应立即作出反应)。我已经设置了读取,写入和进度回调来读取原子should_cancel变量(如this问题),但有两个问题:

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

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

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

    湾调用进程回调 ,我返回非零值(意味着它应该终止),但curl操作暂时不会完成(事实上,在此期间再次调用progress函数) !)

  3. 所以:

    1. 为什么会出现长时间的延迟(特别是没有调用进度函数)?
    2. 我该怎么办才能让取消按钮正确反应?
    3. 一种可能性是告诉UI取消操作成功,但继续在后台运行curl线程,直到取消。这个问题(我认为)是它强制should_cancel变量是全局的,而不是作用于操作开始的对话框。

3 个答案:

答案 0 :(得分:9)

我遇到了卷曲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
        }
...

答案 1 :(得分:4)

你的基本想法是正确的。您应该从UI分离curl操作。但是,实现应该稍微改变一下。您不应该使用全局should_cancel。相反,您应该有一个全局current_request指针,指向Request类型的对象。此类型应具有内部cancel标志和公共Cancel()功能。响应取消按钮,您在Cancel上调用current_request,然后将其取消。然后,取消的请求可以在以后进行自己的清理(毕竟这是一个线程)。

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

答案 2 :(得分:1)

延迟可能发生,因为curl_easy_perform忙于写回读或回读,尝试从写回调返回0或从读回调返回CURL_READFUNC_ABORT。根据文档,如果写回调返回的值与函数接收的值不同,则传输将被中止,如果读回调返回CURL_READFUNC_ABORT,则传输也将中止(从版本7.12开始有效) .1的图书馆)。