c++ - 在异步模式下使用的 WinHttp - ERROR_INTERNET_CANNOT_CONNECT 如何干净地关闭连接

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

这里buffer是一个vector,一旦发起请求就预留了8K。提前致谢。

在 OnResponseComplete 和 OnResponsEerror 中,我最终还调用了:

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

最佳答案

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

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

关于c++ - 在异步模式下使用的 WinHttp - ERROR_INTERNET_CANNOT_CONNECT 如何干净地关闭连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11577503/

相关文章:

Python 套接字和 Opencv - ConnectionResetError : [WinError 10054]

c++ - 函数指针的 Type(f)(Type) 和 Type(*f)(Type) 之间的区别?

c++ - 如何在 Fedora 的 C++ 中包含 avcodec.h

windows - 手动卸载后无法通过注册表重新安装 Chrome 扩展

c - scanf_s 读取字符我做错了什么?

c++ - 在 Windows 中使用 boost::program_options 从命令行参数读取 Unicode 字符

git - 如何制作 git 存储库 'pull only'

networking - TCP的重传规则是什么?

c++ - 在一个声明语句中定义一个函数和一个变量

C++ 取消引用发生在隐式转换之后