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;
        }
    }
}

这里的缓冲区是一个向量,一旦发起请求,就保留了 8K。提前致谢。

在 OnResponseComplete, OnResponsEerror 我最终也调用:

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

2 回答 2

3

Roman R 对这个问题的看法是正确的,只是想在回复中包含更多细节。取消是棘手的。对于 WinHTTP,您需要记住回调在线程池上触发。它们可以到达随机线程,并且由于线程池不保证回调何时执行,您可以调用 WinHttpSetStatusCallback 清除回调,然后稍后仍会接收回调。因此,您需要自己同步它。一个更微妙的问题是您不能从回调中回调 WinHTTP。处理 WinHTTP 回调的最安全和最可靠的方法是将回调分派到单个线程。这可以是 UI 线程或单线程线程池。

到目前为止,我的测试表明,关闭请求句柄必须在回调之外完成,并且最终关闭连接句柄。可以将关闭请求的意图排队,然后使用 QueueUserApc 在单独的线程上关闭连接和其他清理,以便至少同步这两个操作。

于 2012-07-25T20:36:52.283 回答
0

编辑 问题仍然存在。我在下面写的并没有解决问题。

所描述的问题实际上与 ERROR_CANNOT_CONNECT 并没有太大关系,而是与我“错误地”处理 winhttp 的异步状态回调通知的方式有关。如果有人有兴趣,我将在此处复制处理状态通知的典型方式。

于 2012-07-20T22:15:57.933 回答