3

リクエストのコールバック プロシージャで ERROR_INTERNET_CANNOT_CONNECT (12029 コード) が大量に発生します。(サーバー上で) 非同期モードで WinHttp を使用します。この場合、接続をきれいに閉じるにはどうすればよいですか。次のようなものを使用しますか(通常は接続を閉じますか?):

            ::WinHttpSetStatusCallback(handle, NULL, 0, 0);
            ::WinHttpCloseHandle(this->handle));

説明されている状況で発生するwinhttp dllに関連する奇妙なメモリリークがあるため、これを尋ねます(おそらく会社の内部ファイアウォールによってブロックされるか、宛先サーバーが接続をドロップする数百の同時接続を作成したい)。私はすでにmsdnのWinHttpCloseHandleのドキュメントを見てきました...

コールバック状態の処理方法は次のとおりです。

    template <typename T>
void WinHttp::AsyncRequest<T>::OnCallback(DWORD code, const void* info, DWORD length)
{
    T* pT = static_cast<T*>(this);

    switch (code)
    {
    case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
    case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
        {
            HRESULT result = pT->OnWriteData();
            if (FAILED(result))
            {
                throw CommunicationException(::GetLastError());
            }
            if (S_FALSE == result)
            {
                if (!::WinHttpReceiveResponse(handle, 0)) // reserved
                {
                    throw CommunicationException(::GetLastError());
                }
            }
            break;
        }

    case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
        {
            DWORD statusCode;
            DWORD statusCodeSize = sizeof(DWORD);
            if (!::WinHttpQueryHeaders(handle, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode, &statusCodeSize, WINHTTP_NO_HEADER_INDEX))
            {
                throw CommunicationException(::GetLastError());
            }
            pT->OnStatusCodeReceived(statusCode);
            if (!::WinHttpQueryDataAvailable(handle, 0))
            {
                // If a synchronous error occured, throw error.  Otherwise
                // the query is successful or asynchronous.
                throw CommunicationException(::GetLastError());
            }

            break;
        }

    case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
        {
            unsigned int size = *((LPDWORD) info);
            if (size == 0)
            {
                pT->OnResponseComplete(S_OK);
            }
            else
            {
                unsigned int sizeToRead = (size <= chunkSize) ? size : chunkSize;
                if (!::WinHttpReadData(handle, &buffer[0], sizeToRead, 0)) // async result
                {
                    throw CommunicationException(::GetLastError());
                }
            }
            break;
        }

    case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
        {
            if (length != 0)
            {
                pT->OnReadComplete(&buffer[0], length);
                if (!::WinHttpQueryDataAvailable(handle, 0))
                {
                    // If a synchronous error occured, throw error.  Otherwise
                    // the query is successful or asynchronous.
                    throw CommunicationException(::GetLastError());
                }
            }
            else
            {
                pT->OnResponseComplete(S_OK);
            }

            break;
        }

    case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
        {
            {
                throw CommunicationException(::GetLastError());
            }

            break;
        }
    }
}

ここで buffer は、リクエストが開始されたときに 8K を予約したベクターです。前もって感謝します。

OnResponseComplete、OnResponsEerror では、最終的に次のようにも呼び出します。

::WinHttpSetStatusCallback(handle, NULL, 0, 0);
 assert(::WinHttpCloseHandle(this->handle));
 this->handle = nullptr;
4

2 に答える 2

3

ローマン R は、応答に詳細を含めたいという問題について正しかったです。キャンセルは面倒です。WinHTTP の場合、コールバックはスレッド プールで発生することを覚えておく必要があります。それらはランダム スレッドで到着する可能性があり、スレッド プールはいつコールバックが実行されるかを保証しないため、WinHttpSetStatusCallback を呼び出してコールバックをクリアし、後でコールバックを受信することができます。したがって、これを自分で同期する必要があります。より微妙な問題は、コールバックから WinHTTP にコールバックできないことです。WinHTTP コールバックを処理する最も安全で信頼性の高い方法は、コールバックを単一のスレッドにディスパッチすることです。これは、UI スレッドまたはシングルスレッド スレッド プールの可能性があります。

これまでのテストでは、リクエスト ハンドルのクローズはコールバックの外で行う必要があり、接続ハンドルの最終的なクローズも行う必要があることがわかりました。少なくともこれら 2 つの操作が同期されるように、QueueUserApc を使用して別のスレッドで、要求を閉じる意図をキューに入れ、その後接続を閉じてその他のクリーンアップを行うことができます。

于 2012-07-25T20:36:52.283 に答える
0

編集 問題はまだ残っています。以下に書いたことは問題を解決しませんでした。

説明されている問題は、実際には ERROR_CANNOT_CONNECT とはあまり関係がありませんでしたが、winhttp の非同期ステータス コールバック通知を「誤って」処理していたことに関係していました。興味のある方は、ステータス通知を処理する一般的な方法をここにコピーします。

于 2012-07-20T22:15:57.933 に答える