如何立即取消 curl 操作?

Posted

技术标签:

【中文标题】如何立即取消 curl 操作?【英文标题】:How can I immediately cancel a curl operation? 【发布时间】:2011-05-26 06:22:06 【问题描述】:

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

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

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

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

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

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

所以:

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

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

【问题讨论】:

使用非阻塞 curl_multi_perform 怎么样?curl.haxx.se/libcurl/c/curl_multi_perform.html 【参考方案1】:

我在 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
        
...

【讨论】:

可能手动入侵和关闭套接字是真正立即连接中断的最佳选择,该操作的线程安全性加分。 (永远不要尝试curl_easy_cleanup 可能正在由另一个线程主动curl_easy_/perform/recv/send/'ed ..但是使用套接字很好并且curl不会崩溃!)。您可能对当前 curl 可以通过 curl_socket_t sockfd; curl_easy_getinfo(handle, CURLINFO_ACTIVESOCKET, &sockfd); 轻松使用套接字这一事实感兴趣,因此您可以随时访问 curl 句柄来获取它。【参考方案2】:

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

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

【讨论】:

等等,什么?不.. 他应该使用 curl 多 API。并使用全局原子布尔值来停止线程中的 (while) 循环。他/她应该使用以下函数,而不是调用curl_easy_performcurl_multi_performcurl_multi_pollcurl_multi_cleanup。这允许您在任何给定时间停止请求。【参考方案3】:

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

【讨论】:

我是!我将“取消”返回代码传递给所有三个回调(读取、写入和进度),但在第一个“长时间延迟”情况下没有调用任何回调。 我认为你应该重写你的线程代码。不要使用curl_easy_perform,而是使用 curl 多 API。喜欢:curl_multi_performcurl_multi_pollcurl_multi_cleanup

以上是关于如何立即取消 curl 操作?的主要内容,如果未能解决你的问题,请参考以下文章

PHP cURL取消订阅PayPal问题

如何找出 CancelIo() 何时完成?

压缩后如何立即使用 Phar 解压缩存档?

Flutter中的节流与防抖(过滤重复点击)

如何立即从范围内取消Kotlin协程(返回)?]

怎么取消百度登录验证