异步模式下使用的WinHttp-ERROR_INTERNET_CANNOT_CONNECT如何干净地关闭连接
WinHttp used in async mode - ERROR_INTERNET_CANNOT_CONNECT how to cleanly close connection
在请求的回调过程中,我得到了很多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;
Roman R对这个问题是正确的,只是想在响应中包含更多细节。取消很棘手。对于WinHTTP,您需要记住回调是在线程池上触发的。它们可以在随机线程上到达,由于线程池不能保证何时执行回调,您可以调用WinHttpSetStatus callback来清除回调,然后稍后仍然接收回调。因此,您需要自己同步。一个更微妙的问题是,您无法从回调中回调回WinHTTP。处理WinHTTP回调最安全、最可靠的方法是将回调调度到单个线程。这可以是一个UI线程或一个单线程线程池。然后,您还需要先关闭句柄,然后等待回调来指示句柄已关闭(WINHTTP_callback_STATUSHANDLE_CLOSING)——只有这样,才可以安全地释放任何特定于连接的资源、回调等。
到目前为止,我的测试表明,关闭请求句柄必须在回调之外完成,也必须在连接句柄的最终关闭之外完成。可以使用QueueUserApc在单独的线程上对关闭请求的意图以及随后的连接关闭和其他清理进行排队,以便至少同步这两个操作。
编辑问题仍然存在。我在下面写的并没有解决这个问题。
所描述的问题实际上与ERROR_CANNOT_CONNECT没有太大关系,而是与我"不正确"处理winhttp的异步状态回调通知的方式有关。如果有人感兴趣,我会在这里复制一种处理状态通知的典型方法。
- Eigen如何在容器循环中干净地附加矩阵
- 函数何时会在c++中包含stack_Unwind_Resume调用
- Python中的for循环与C++有何不同
- 在C++中迭代 2D 容器的最干净方法
- WinSock2:connect() 提供"连接被拒绝"
- 在C++中释放内存期间,迭代器与指针有何不同
- 如何使用connect将qml按钮与同一类的cpp函数连接起来
- 在 QTextEdit 中使用指针或在 Qt-Creator 上使用 connect()
- 比较C++中两个整数的最有效和最干净的方法是什么?
- 标准对此指向成员函数类型模板参数有何说明?是我的代码有误,还是 MSVS 16.6 有问题?
- C++将 ifloat 表定义为数组的干净方法
- gnuradio c++ connect self() throw bad_weak_ptr
- C++中的多维数据集:从 std::vector 的 2D 数据到 std::vector 的 2D 网格的最干净方法?
- 类中的 C++ int 被设置为值,似乎不知从何而来
- -fvisibility-inline-hidden 与 gcc 中的 -fvisibility=hidden 有何不同
- 如何将函数指针传递给方法,以便它可以在Qt的connect()中使用?
- 在CTOR vs Connect方法中提升套接字与stream_socket端点
- Make zmqpp::socket::connect a std::future
- 将lambda放入类定义中的干净方法
- C-Free and MySQL connect