如何立即取消卷曲操作?

Jes*_*der 6 c++ multithreading curl boost-thread

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

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

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

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

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

    湾 进度回调调用时,我返回一个非零值(这意味着它应该终止),但卷曲操作不一会儿完成更长(事实上,功能将被再次在此期间,被称为!)

所以:

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

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

fan*_*ory 10

我遇到了卷曲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
        }
...
Run Code Online (Sandbox Code Playgroud)

  • 手动侵入并关闭套接字可能是真正立即连接中断的最佳选择,这是该操作的线程安全性的加分。(永远不要在可能由另一个线程主动进行curl_easy_/perform/recv/send/'ed的事情上尝试“curl_easy_cleanup”。但是使用套接字就可以了,而且curl不会崩溃!)。您可能对以下事实感兴趣:目前,curl 使套接字可以通过“curl_socket_t sockfd;”轻松使用。curl_easy_getinfo(handle, CURLINFO_ACTIVESOCKET, &sockfd);` 这样您就可以随时访问curl句柄来获取它。 (2认同)

MSa*_*ers 5

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

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