12

C ++でlibcurlを使用しており、 Boost.Threadcurl_easy_performを使用してUIとは別のスレッドで呼び出しています。

メインUIには、完全に応答したいキャンセルボタンがあります(つまり、ユーザーがクリックすると、すぐに反応するはずです)。(このshould_cancel質問のように)アトミック変数を読み取るように設定された読み取り、書き込み、および進行状況のコールバックがありますが、2つの問題があります。

  1. 多くの場合、キャンセルが押されてからカール操作が完了するまで、非常に小さな(ただし目立つ)遅延があります。

  2. 時折、非常に長い(時には途方もない)遅延があります。この場合、次のいずれかです。

    a。進行状況、読み取り、および書き込みのコールバックは、単に長い間呼び出されないか、または

    b。progressコールバック呼び出され、ゼロ以外の値が返されます(つまり、終了する必要があります)が、curl操作がしばらく完了しません(実際、progress関数がその間に再度呼び出されます!)

それで:

  1. なぜ長い遅延が発生するのですか(特にprogress関数を呼び出さずに)?
  2. キャンセルボタンが正しく反応するようにするには、代わりに何をすればよいですか?

1つの可能性は、キャンセル操作が成功したことをUIに通知することですが、キャンセルされるまでバックグラウンドでカールスレッドを実行し続けます。これに伴う問題(私は思う)は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_cancelcurrent_request代わりに、タイプ のオブジェクトへのグローバル ポインタが必要ですRequest。この型には、内部cancelフラグとパブリックCancel()関数が必要です。キャンセル ボタンに応答して、 を呼び出しCancelcurrent_requestから null にします。キャンセルされたリクエストは、後で独自のクリーンアップを行う責任があります (結局のところ、それはスレッドです)。

ゾンビ オブジェクトを防ぐために、ミューテックスに注意する必要があります。キャンセルとリクエストの完了の間には固有の競合状態があります。

于 2010-12-16T10:06:50.800 に答える
1

curl_easy_performが書き込みコールバックまたは読み取りコールバックでビジー状態であり、書き込みコールバックまたは読み取りコールバックから 0 を返そうとすると、遅延が発生する可能性がありCURL_READFUNC_ABORTます。ドキュメントによると、書き込みコールバックによって返された値が関数によって受信された値と異なる場合、転送は中止され、読み取りコールバックが返された場合CURL_READFUNC_ABORT、転送も中止されます (ライブラリのバージョン 7.12.1 から有効) )。

于 2010-12-16T00:34:53.193 に答える