如何立即取消 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_perform
:curl_multi_perform
、curl_multi_poll
和curl_multi_cleanup
。这允许您在任何给定时间停止请求。【参考方案3】:
延迟可能是因为curl_easy_perform
忙于写入或读取回调,尝试从写入回调返回 0 或从读取回调返回 CURL_READFUNC_ABORT
。根据文档,如果写回调返回的值与函数接收到的值不同,传输将被中止,如果读回调返回CURL_READFUNC_ABORT
,传输也将被中止(从版本 7.12.1 开始有效图书馆)。
【讨论】:
我是!我将“取消”返回代码传递给所有三个回调(读取、写入和进度),但在第一个“长时间延迟”情况下没有调用任何回调。 我认为你应该重写你的线程代码。不要使用curl_easy_perform
,而是使用 curl 多 API。喜欢:curl_multi_perform
、curl_multi_poll
和 curl_multi_cleanup
。以上是关于如何立即取消 curl 操作?的主要内容,如果未能解决你的问题,请参考以下文章