WinHttp用于异步模式 - ERROR_INTERNET_CANNOT_CONNECT如何干净地关闭连接

时间:2012-07-20 10:26:54

标签: c++ windows networking winhttp

我在请求的回调过程中获得了大量的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;

2 个答案:

答案 0 :(得分:2)

Roman R关于这个问题是正确的,只是想在回复中包含更多细节。 取消是棘手的。对于WinHTTP,您需要记住在线程池上触发回调。它们可以到达一个随机线程,因为线程池不能保证何时执行回调,你可以调用WinHttpSetStatusCallback来清除回调,然后再继续接收回调。因此,您需要自己同步。一个更微妙的问题是你无法通过回调回调到WinHTTP。处理WinHTTP回调的最安全和最可靠的方法是将回调分派给单个线程。这可以是UI线程或单线程线程池。然后,您还需要先关闭句柄,然后等待回调以指示句柄已关闭(WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) - 只有这样才能安全地释放任何特定于连接的资源,回调等。

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

答案 1 :(得分:0)

修改 问题仍然存在。我在下面写的内容没有解决问题。

所描述的问题实际上与ERROR_CANNOT_CONNECT没有多大关系,而是与我“无情地”处理winhttp的异步状态回调通知的方式有关。如果有人有兴趣,我会在这里复制处理状态通知的典型方法。