您的位置:首页 > 文旅 > 美景 > 长沙做网站的有哪些_济宁检出阳性259人_陕西省人民政府_百度平台联系方式

长沙做网站的有哪些_济宁检出阳性259人_陕西省人民政府_百度平台联系方式

2024/12/23 9:33:00 来源:https://blog.csdn.net/Flame_Cyclone/article/details/142644088  浏览:    关键词:长沙做网站的有哪些_济宁检出阳性259人_陕西省人民政府_百度平台联系方式
长沙做网站的有哪些_济宁检出阳性259人_陕西省人民政府_百度平台联系方式

CWinHttpClient.h

#pragma once#include <Windows.h>
#include <WinHttp.h>
#include <stdint.h>
#include <string>
#include <vector>
#include <functional>
#include <map>
#include <set>
#include <thread>
#include <time.h>
#include <tchar.h>#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif#pragma comment(lib, "winhttp.lib")
#define WINHTTP_AGENT_HTTPS "(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0)"// 响应结果
class CWinHttpResult
{
public:CWinHttpResult() :code(0) {}std::string result;             //响应结果uint32_t code;                  //响应状态码
};// 进度信息
typedef struct _WINHTTP_PROGRESS_INFO
{ULONGLONG ullCur;           // 当前下载量(字节)ULONGLONG ullTotal;         // 总数据量(字节)double lfProgress;          // 当前进度(0.0f - 1.0f)double lfSpeed;             // 当前速度(字节/秒)double lfRemainTime;        // 剩余时间(毫秒)clock_t costTime;           // 消耗时间(毫秒)uint32_t nActiveThread;     // 活动任务数uint32_t nTotalThread;      // 总任务线程数
}WINHTTP_PROGRESS_INFO, *LPWINHTTP_PROGRESS_INFO;// 异步数据
typedef struct _WHTTP_ASYNC_DATA
{DWORD_PTR dwContext;                // 上下文HANDLE hEvent;                      // 事件DWORD dwWait;                       // 事件等待结果DWORD dwSize;                       // 传输数据大小WINHTTP_ASYNC_RESULT AsyncResult;   // 异步结果
}WHTTP_ASYNC_DATA, *LPWHTTP_ASYNC_DATA;// 请求头信息
using WinHttpRequestHeader = std::map<_tstring, std::set<_tstring>>;// 进度回调
using WinHttpProgressCallback = std::function<bool(const WINHTTP_PROGRESS_INFO& progress)>;// WinHttp客户端辅助类
class CWinHttpClient
{
public:CWinHttpClient();~CWinHttpClient();// 关闭 void Close();// 发送 GET 请求CWinHttpResult Get(const _tstring& strUrl, WinHttpProgressCallback cb = nullptr);// 发送 POST 请求CWinHttpResult Post(const _tstring& strUrl, const std::string& strParam, WinHttpProgressCallback cb = nullptr);// 发送 GET 请求(多线程)CWinHttpResult GetEx(const _tstring& strUrl, WinHttpProgressCallback cb = nullptr, DWORD dwThreadCount = 1);// 多线程下载文件bool DownloadFile(const _tstring& strUrl, const _tstring& strFile = _T(""), WinHttpProgressCallback cb = nullptr, DWORD dwThreadCount = 4);// 添加请求头信息void AddRequestHeader(const _tstring strCaption, const _tstring strData);// 移除请求头信息void RemoveRequestHeader(const _tstring strCaption, const _tstring strData);// 清除请求头信息void ClearRequestHeader();// 设置用户代理字符串void SetAgent(const _tstring strAgent = _T(WINHTTP_AGENT_HTTPS));// 设置请求数据范围void SetReQuestDataRange(ULONGLONG ullStart = 0, ULONGLONG ullLength = -1);// 设置异步状态打印void SetPrintStatus(bool fEnable = true);public:// 字符串转UTF-8编码字符串static std::string TStrToU8Str(const _tstring& str);// 字符串转宽字节字符串static std::wstring TStrToWStr(const _tstring& str);// 宽字节字符串转字符串static _tstring WStrToTStr(const std::wstring& str);// 控制台打印static void ConsoleOutput(LPCTSTR pFormat, ...);private:// 获取请求头字符串_tstring _GetRequestHeaderString();// 执行请求CWinHttpResult _DoRequest(const _tstring& strUrl, const _tstring& strMethod, const std::string& strParam);// 执行请求(多线程)CWinHttpResult _GetEx(const _tstring& strUrl, WinHttpProgressCallback cb = nullptr, DWORD dwThreadCount = 1);CWinHttpResult _MultiThreadRequest(const _tstring& strUrl, ULONGLONG ullContentLength, DWORD dwThreadCount = 1);// 读取网络流bool _InternetReadData(std::string& strData);// 查询资源大小bool _QueryContentLength(PULONGLONG lpUllContentLength);// 查询是否支持接收范围bool _IsSupportAcceptRanges();// 设置请求数据范围bool _SetRequestDataRange(LONGLONG nBegin, LONGLONG nLength);// 获取状态码DWORD _GetStatusCode(HINTERNET hRequest);// 错误输出void _PrintError(LPCTSTR lpszError) const;// 状态码打印static void _PrintStatus(DWORD dwCode, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength);// 状态回调static VOID CALLBACK WinHttpStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength);// 状态回调void _WinHttpStatusCallback(HINTERNET hInternet, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength);// 等待异步事件bool _WaitForAsyncEvent(DWORD dwMilliseconds = INFINITE);// 打开会话bool _WinHttpOpen(LPCWSTR pszAgentW, DWORD dwAccessType, LPCWSTR pszProxyW, LPCWSTR pszProxyBypassW, DWORD dwFlags);// 设置会话超时bool _WinHttpSetSessionTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout);// 设置请求超时bool _WinHttpSetRequestTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout);// 设置状态回调bool _WinHttpSetStatusCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags);// 设置会话选项bool _WinHttpSetSessionOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength);// 设置请求选项bool _WinHttpSetRequestOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength);// 连接会话bool _WinHttpConnect(LPCWSTR pswzServerName, INTERNET_PORT nServerPort);// 打开请求bool _WinHttpOpenRequest(const _tstring& strVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags);// 发送请求bool _WinHttpSendRequest(_tstring strHeader, LPVOID lpData, DWORD dwSize, DWORD_PTR dwContext);// 查询请求头bool _WinHttpQueryHeaders(DWORD dwInfoLevel, LPCWSTR pwszName, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex);// 接收响应数据bool _WinHttpReceiveResponse(HINTERNET hRequest);// 查询请求数据是否可用bool _WinHttpQueryDataAvailable(LPDWORD lpdwNumberOfBytesAvailable);// 读取请求数据bool _WinHttpReadData(LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead);// 宽字节字符串转多字节字符串static std::string _WStrToMultiStr(UINT CodePage, const std::wstring& str);// 多字节字符串转宽字节字符串static std::wstring _MultiStrToWStr(UINT CodePage, const std::string& str);private:HINTERNET m_hSession;                           // 会话句柄HINTERNET m_hConnect;                           // 连接句柄HINTERNET m_hRequest;                           // 请求句柄WinHttpRequestHeader    m_RequestHeader;        // 请求头WinHttpProgressCallback m_cbProgress;           // 进度回调WHTTP_ASYNC_DATA        m_AsyncData;            // 异步信息_tstring                m_strAgent;             // 代理字符串_tstring                m_strFilePath;          // 保存文件路径ULONGLONG               m_ullStart;             // 请求数据起始位置ULONGLONG               m_ullLength;            // 请求长度ULONGLONG               m_ullDownload;          // 已下载数据bool                    m_fAbort;               // 终止bool                    m_fPrint;               // 打印进度
};

CWinHttpClient.cpp

#include "CWinHttpClient.h"
#include <strsafe.h>
#include <thread>
#include <atomic>typedef struct _WINHTTP_URL_INFO
{std::wstring strScheme;std::wstring strHostName;std::wstring strUserName;std::wstring strPassword;std::wstring strUrlPath;std::wstring strExtraInfo;URL_COMPONENTS uc = { 0 };_WINHTTP_URL_INFO(){memset(&uc, 0, sizeof(uc));try{strScheme.resize(32);strHostName.resize(128);strUserName.resize(128);strPassword.resize(128);strUrlPath.resize(2048);strExtraInfo.resize(512);this->uc.dwStructSize = sizeof(this->uc);this->uc.lpszScheme = &this->strScheme[0];this->uc.dwSchemeLength = (DWORD)strScheme.size();this->uc.lpszHostName = &this->strHostName[0];this->uc.dwHostNameLength = (DWORD)strHostName.size();this->uc.lpszUserName = &this->strUserName[0];this->uc.dwUserNameLength = (DWORD)strUserName.size();this->uc.lpszPassword = &this->strPassword[0];this->uc.dwPasswordLength = (DWORD)strPassword.size();this->uc.lpszUrlPath = &this->strUrlPath[0];this->uc.dwUrlPathLength = (DWORD)strUrlPath.size();this->uc.lpszExtraInfo = &this->strExtraInfo[0];this->uc.dwExtraInfoLength = (DWORD)strExtraInfo.size();}catch (...){}}
}WINHTTP_URL_INFO, *PWINHTTP_URL_INFO;CWinHttpClient::CWinHttpClient():m_hSession(NULL),m_hConnect(NULL),m_hRequest(NULL),m_fAbort(false),m_fPrint(false),m_ullStart(0),m_ullLength(-1),m_ullDownload(0),m_AsyncData{ 0 }
{m_AsyncData.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
}CWinHttpClient::~CWinHttpClient()
{Close();if (NULL != m_AsyncData.hEvent){::CloseHandle(m_AsyncData.hEvent);}
}void CWinHttpClient::Close()
{m_fAbort = true;if (NULL != m_AsyncData.hEvent){::SetEvent(m_AsyncData.hEvent);}if (m_hRequest){::WinHttpCloseHandle(m_hRequest);m_hRequest = NULL;}if (m_hConnect){::WinHttpCloseHandle(m_hConnect);m_hConnect = NULL;}if (m_hSession){::WinHttpCloseHandle(m_hSession);m_hSession = NULL;}m_fAbort = false;
}static bool _CrackUrl(const _tstring& strUrl, PWINHTTP_URL_INFO lpUrlInfo)
{// 将 URL 分解到其组件部件中// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpcrackurl// 即使在异步模式下使用 WinHTTP (即在 WinHttpOpen) 中设置了WINHTTP_FLAG_ASYNC时,此函数也会同步运行。if (!::WinHttpCrackUrl(CWinHttpClient::TStrToWStr(strUrl).c_str(), 0, 0, &lpUrlInfo->uc)){return false;}lpUrlInfo->strScheme.resize(wcslen(lpUrlInfo->strScheme.c_str()));lpUrlInfo->strExtraInfo.resize(wcslen(lpUrlInfo->strExtraInfo.c_str()));lpUrlInfo->strHostName.resize(wcslen(lpUrlInfo->strHostName.c_str()));lpUrlInfo->strUserName.resize(wcslen(lpUrlInfo->strUserName.c_str()));lpUrlInfo->strPassword.resize(wcslen(lpUrlInfo->strPassword.c_str()));lpUrlInfo->strUrlPath.resize(wcslen(lpUrlInfo->strUrlPath.c_str()));if (!lpUrlInfo->strExtraInfo.empty()){lpUrlInfo->strUrlPath += lpUrlInfo->strExtraInfo;}// 协议检查if (!(INTERNET_SCHEME_HTTPS == lpUrlInfo->uc.nScheme || INTERNET_SCHEME_HTTP == lpUrlInfo->uc.nScheme)){return false;}return true;
}CWinHttpResult CWinHttpClient::Get(const _tstring& strUrl, WinHttpProgressCallback cb/* = nullptr*/)
{m_cbProgress = cb;return _DoRequest(strUrl, _T("GET"), "");
}CWinHttpResult CWinHttpClient::Post(const _tstring& strUrl, const std::string& strParam, WinHttpProgressCallback cb/* = nullptr*/)
{m_cbProgress = cb;return _DoRequest(strUrl, _T("POST"), strParam);
}CWinHttpResult CWinHttpClient::GetEx(const _tstring& strUrl, WinHttpProgressCallback cb/* = nullptr*/, DWORD dwThreadCount/* = 1*/)
{m_strFilePath.clear();return _GetEx(strUrl, cb, dwThreadCount);
}bool CWinHttpClient::DownloadFile(const _tstring& strUrl, const _tstring& strFile, WinHttpProgressCallback cb/* = nullptr*/, DWORD dwThreadCount/* = 4*/)
{m_strFilePath = strFile;if (m_strFilePath.empty()){size_t nPos = strUrl.find_last_of(_T("/"));if (_tstring::npos != nPos){m_strFilePath = strUrl.substr(nPos + 1);}}// 清空文件HANDLE hFile = INVALID_HANDLE_VALUE;hFile = ::CreateFile(strFile.c_str(),GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE,NULL,TRUNCATE_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL);if (INVALID_HANDLE_VALUE != hFile){::CloseHandle(hFile);}CWinHttpResult httpResult = _GetEx(strUrl, cb, dwThreadCount);return true;
}CWinHttpResult CWinHttpClient::_DoRequest(const _tstring& strUrl, const _tstring& strMethod, const std::string& strParam)
{WINHTTP_URL_INFO urlInfo;CWinHttpResult httpResult;m_strFilePath.clear();if (!_CrackUrl(strUrl, &urlInfo)){_PrintError(_T("_CrackUrl"));return httpResult;}do{// 初始化 HTTP 会话std::wstring wstrAgent = TStrToWStr(m_strAgent);DWORD dwAccessType = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY;LPCWSTR pszProxy = WINHTTP_NO_PROXY_NAME;LPCWSTR pszProxyBypass = WINHTTP_NO_PROXY_BYPASS;DWORD dwSessionFlags = WINHTTP_FLAG_ASYNC;    //WINHTTP_FLAG_ASYNC / WINHTTP_FLAG_SECURE_DEFAULTSif (!_WinHttpOpen(wstrAgent.c_str(), dwAccessType, pszProxy, pszProxyBypass, dwSessionFlags)){_PrintError(_T("_WinHttpOpen"));break;}// 设置回调函数if (!_WinHttpSetStatusCallback(WinHttpStatusCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS)){_PrintError(_T("WinHttpSetStatusCallback"));break;}// 设置状态回调上下文DWORD_PTR dwContext = (DWORD_PTR)this;if (!_WinHttpSetSessionOption(WINHTTP_OPTION_CONTEXT_VALUE, &dwContext, sizeof(dwContext))){_PrintError(_T("WinHttpSetOption"));break;}// 连接 HTTP 会话if (!_WinHttpConnect(urlInfo.strHostName.c_str(), urlInfo.uc.nPort)){_PrintError(_T("_WinHttpConnect"));break;}// 创建 HTTP 请求句柄LPCWSTR pwszReferrer = WINHTTP_NO_REFERER;LPCWSTR* pwszAcceptTypes = WINHTTP_DEFAULT_ACCEPT_TYPES;DWORD dwRequestFlags = (INTERNET_SCHEME_HTTPS == urlInfo.uc.nScheme) ? WINHTTP_FLAG_SECURE : 0;if (!_WinHttpOpenRequest(strMethod, urlInfo.strUrlPath.c_str(), NULL, pwszReferrer, pwszAcceptTypes, dwRequestFlags)){_PrintError(_T("_WinHttpOpenRequest"));break;}// 设置请求超时if (!_WinHttpSetRequestTimeouts(500, 500, 1000, 1000)){_PrintError(_T("WinHttpSetTimeouts"));break;}// 设置请求范围if (!_SetRequestDataRange(m_ullStart, m_ullLength)){_PrintError(_T("_SetRequestDataRange"));break;}// 发送请求_tstring strHeader = _GetRequestHeaderString();if (!_WinHttpSendRequest(strHeader, (LPVOID)strParam.data(), (DWORD)strParam.size(), (DWORD_PTR)this)){_PrintError(_T("_WinHttpSendRequest"));break;}// 等待接收请求响应if (!_WinHttpReceiveResponse(m_hRequest)){_PrintError(_T("_WinHttpReceiveResponse"));break;}// 查询文件大小ULONGLONG UllContentLength = 0;if (!_QueryContentLength(&UllContentLength)){_PrintError(_T("_QueryContentLength"));break;}// 获取响应码httpResult.code = _GetStatusCode(m_hRequest);if (httpResult.code < 200 || httpResult.code >= 300){_PrintError(_T("_GetStatusCode"));break;}// 接收请求数据if (!_InternetReadData(httpResult.result)){_PrintError(_T("_InternetReadData"));break;}} while (false);// 关闭相关句柄Close();return httpResult;
}CWinHttpResult CWinHttpClient::_GetEx(const _tstring& strUrl, WinHttpProgressCallback cb/* = nullptr*/, DWORD dwThreadCount/* = 6*/)
{WINHTTP_URL_INFO urlInfo;CWinHttpResult httpResult;if (!_CrackUrl(strUrl, &urlInfo)){_PrintError(_T("_CrackUrl"));return httpResult;}m_cbProgress = cb;do{// 初始化 HTTP 会话std::wstring wstrAgent = TStrToWStr(m_strAgent);DWORD dwAccessType = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY;LPCWSTR pszProxy = WINHTTP_NO_PROXY_NAME;LPCWSTR pszProxyBypass = WINHTTP_NO_PROXY_BYPASS;DWORD dwSessionFlags = WINHTTP_FLAG_ASYNC;    //WINHTTP_FLAG_ASYNC / WINHTTP_FLAG_SECURE_DEFAULTSif (!_WinHttpOpen(wstrAgent.c_str(), dwAccessType, pszProxy, pszProxyBypass, dwSessionFlags)){_PrintError(_T("_WinHttpOpen"));break;}// 设置回调函数if (!_WinHttpSetStatusCallback(WinHttpStatusCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS)){_PrintError(_T("WinHttpSetStatusCallback"));break;}// 设置状态回调上下文DWORD_PTR dwContext = (DWORD_PTR)this;if (!_WinHttpSetSessionOption(WINHTTP_OPTION_CONTEXT_VALUE, &dwContext, sizeof(dwContext))){_PrintError(_T("WinHttpSetOption"));break;}// 连接 HTTP 会话if (!_WinHttpConnect(urlInfo.strHostName.c_str(), urlInfo.uc.nPort)){_PrintError(_T("_WinHttpConnect"));break;}// 创建 HTTP 请求句柄LPCWSTR pwszReferrer = WINHTTP_NO_REFERER;LPCWSTR* pwszAcceptTypes = WINHTTP_DEFAULT_ACCEPT_TYPES;DWORD dwRequestFlags = (INTERNET_SCHEME_HTTPS == urlInfo.uc.nScheme) ? WINHTTP_FLAG_SECURE : 0;if (!_WinHttpOpenRequest(_T("GET"), urlInfo.strUrlPath.c_str(), NULL, pwszReferrer, pwszAcceptTypes, dwRequestFlags)){_PrintError(_T("_WinHttpOpenRequest"));break;}// 设置请求超时if (!_WinHttpSetRequestTimeouts(1000, 1000, 3000, 5000)){_PrintError(_T("WinHttpSetTimeouts"));break;}// 发送请求_tstring strHeader = _GetRequestHeaderString();if (!_WinHttpSendRequest(strHeader, NULL, 0, (DWORD_PTR)this)){_PrintError(_T("_WinHttpSendRequest"));break;}// 等待接收请求响应if (!_WinHttpReceiveResponse(m_hRequest)){_PrintError(_T("_WinHttpReceiveResponse"));break;}// 获取响应码httpResult.code = _GetStatusCode(m_hRequest);if (httpResult.code < 200 || httpResult.code >= 300){_PrintError(_T("_GetStatusCode"));break;}// 查询文件大小ULONGLONG ullContentLength = 0;if (!_QueryContentLength(&ullContentLength)){_PrintError(_T("QueryContentLength"));break;}// 是否支持接收范围请求bool fAcceptRange =  _IsSupportAcceptRanges();// 多线程接收请求数据if (ullContentLength > 0 && fAcceptRange && dwThreadCount > 1){httpResult = _MultiThreadRequest(strUrl, ullContentLength, dwThreadCount);}else{// 单线程接收请求数据if (!_InternetReadData(httpResult.result)){_PrintError(_T("_InternetReadData"));break;}}} while (false);// 关闭相关句柄Close();return httpResult;
}CWinHttpResult CWinHttpClient::_MultiThreadRequest(const _tstring& strUrl, ULONGLONG ullContentLength, DWORD dwThreadCount/* = 1*/)
{CWinHttpResult httpResult;std::atomic<int> nTaskCount = dwThreadCount;bool fQuit = false;DWORD ullSinglePackageSize = ullContentLength / dwThreadCount;std::vector<CWinHttpClient> vTaskClients;vTaskClients.resize(dwThreadCount);std::vector<CWinHttpResult> vTaskResults;vTaskResults.resize(dwThreadCount);std::vector<std::thread> vTaskThreads;for (DWORD i = 0; i < dwThreadCount; i++){ULONGLONG ullBeginPos = ullSinglePackageSize * i;ULONGLONG ullPackageSize = (i != dwThreadCount - 1) ? ullSinglePackageSize : ullContentLength - ullBeginPos;vTaskClients[i].SetReQuestDataRange(ullBeginPos, ullPackageSize);vTaskClients[i].m_strAgent = m_strAgent;vTaskClients[i].m_RequestHeader = m_RequestHeader;vTaskClients[i].m_strFilePath = m_strFilePath;vTaskClients[i].m_fPrint = m_fPrint;vTaskThreads.emplace_back(std::thread([&vTaskClients, &vTaskResults, i, strUrl, &nTaskCount, &fQuit]() {while (!fQuit){vTaskClients[i].m_AsyncData.AsyncResult.dwResult = 0;vTaskClients[i].m_AsyncData.AsyncResult.dwError = 0;// 修改请求起始位置vTaskClients[i].m_ullStart += vTaskClients[i].m_ullDownload;vTaskResults[i] = vTaskClients[i].Get(strUrl, [](const WINHTTP_PROGRESS_INFO& progress) {return true;});if (vTaskResults[i].code >= 200 && vTaskResults[i].code < 300){break;}}nTaskCount--;}));}// 进度统计用变量clock_t refreshInterval = 1000;clock_t curTime = ::clock();clock_t startTime = curTime;clock_t lastTime = curTime;double lfRemainTime = 0.0f;double lfSpeed = 0.0f;ULONGLONG ullLastDownload = 0;ULONGLONG ullTotalLength = ullContentLength;while (!fQuit){ULONGLONG ullDownloaded = 0;// 进度计算if (m_cbProgress){// 速度计算clock_t curTime = ::clock();if (curTime - lastTime >= refreshInterval  || 0 == nTaskCount){for (auto& item : vTaskClients){ullDownloaded += item.m_ullDownload;}lfSpeed = (double)(ullDownloaded - ullLastDownload) / ((double)refreshInterval / 1000.0f);if (isinf(lfSpeed) || isnan(lfSpeed)){lfSpeed = 0.0f;}// 进度报告{WINHTTP_PROGRESS_INFO progress = { 0 };progress.lfProgress = (double)ullDownloaded / (double)ullTotalLength;progress.ullCur = ullDownloaded;progress.ullTotal = ullTotalLength;progress.lfSpeed = lfSpeed;progress.costTime = curTime - startTime;progress.lfRemainTime = ((double)(ullTotalLength - ullDownloaded)) / lfSpeed;progress.nActiveThread = nTaskCount;progress.nTotalThread = dwThreadCount;if (!m_cbProgress(progress)){fQuit = true;break;}}lastTime = curTime;ullLastDownload = ullDownloaded;}}if (0 == nTaskCount){break;}::Sleep(10);}// 主动退出if (fQuit){for (auto& item : vTaskClients){item.Close();}}// 等待线程结束for (auto& item : vTaskThreads){if (item.joinable()){item.join();}}// 结果拼接if(m_strFilePath.empty()){for (auto& item : vTaskResults){httpResult.result += item.result;if (item.code >= 200 && item.code < 300){httpResult.code = item.code;}else{httpResult.code = 0;}}}return httpResult;
}std::string CWinHttpClient::_WStrToMultiStr(UINT CodePage, const std::wstring& str)
{int cbMultiByte = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, NULL, 0, NULL, 0);std::string strResult(cbMultiByte, 0);size_t nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size(), NULL, NULL);strResult.resize(nConverted);return strResult;
}std::wstring CWinHttpClient::_MultiStrToWStr(UINT CodePage, const std::string& str)
{int cchWideChar = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, NULL);std::wstring strResult(cchWideChar, 0);size_t nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size());strResult.resize(nConverted);return strResult;
}std::string CWinHttpClient::TStrToU8Str(const _tstring& str)
{
#ifdef _UNICODEreturn WStrToMultiStr(CP_UTF8, str);
#elsereturn _WStrToMultiStr(CP_UTF8, _MultiStrToWStr(CP_ACP, str));
#endif
}std::wstring CWinHttpClient::TStrToWStr(const _tstring& str)
{
#ifdef _UNICODEreturn str;
#elsereturn _MultiStrToWStr(CP_ACP, str);
#endif
}_tstring CWinHttpClient::WStrToTStr(const std::wstring& str)
{
#ifdef _UNICODEreturn str;
#elsereturn _WStrToMultiStr(CP_ACP, str);
#endif
}void CWinHttpClient::AddRequestHeader(const _tstring strCaption, const _tstring strData)
{auto itFind = m_RequestHeader.find(strCaption);if (m_RequestHeader.end() == itFind){std::set<_tstring> setData;setData.insert(strData);m_RequestHeader.insert(std::make_pair(strCaption, setData));}else{itFind->second.insert(strData);}
}void CWinHttpClient::RemoveRequestHeader(const _tstring strCaption, const _tstring strData)
{auto itFind = m_RequestHeader.find(strCaption);if (m_RequestHeader.end() == itFind){return;}if (strData.empty()){m_RequestHeader.erase(itFind);}else{itFind->second.erase(strData);if (itFind->second.empty()){m_RequestHeader.erase(itFind);}}
}void CWinHttpClient::ClearRequestHeader()
{m_RequestHeader.clear();
}void CWinHttpClient::SetAgent(const _tstring strAgent)
{m_strAgent = strAgent;
}void CWinHttpClient::SetReQuestDataRange(ULONGLONG ullStart, ULONGLONG ullLength)
{m_ullStart = ullStart;m_ullLength = ullLength;
}void CWinHttpClient::SetPrintStatus(bool fEnable/* = true*/)
{m_fPrint = fEnable;
}_tstring CWinHttpClient::_GetRequestHeaderString()
{_tstring strResult;size_t nItemIndex = 0;size_t nItemCount = m_RequestHeader.size();for (const auto& item : m_RequestHeader){strResult += item.first;strResult += _T(": ");size_t nDataIndex = 0;size_t nDataCount = item.second.size();for (const auto& data : item.second){strResult += data;nDataIndex++;if (nDataIndex < nDataCount){strResult += _T(";");}}nItemIndex++;if (nItemIndex < nItemCount){strResult += _T("\r\n");}}return strResult;
}bool CWinHttpClient::_QueryContentLength(PULONGLONG lpUllContentLength)
{DWORD dwInfoLevel = WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER64;ULONGLONG ullContentLength = 0;DWORD dwBufferLength = sizeof(ullContentLength);DWORD dwIndex = 0;if (!_WinHttpQueryHeaders(dwInfoLevel, WINHTTP_HEADER_NAME_BY_INDEX, &ullContentLength, &dwBufferLength, &dwIndex)){// 无法找到请求的标头if (ERROR_WINHTTP_HEADER_NOT_FOUND == ::GetLastError()){*lpUllContentLength = -1;return true;}_PrintError(_T("WinHttpQueryHeaders"));*lpUllContentLength = 0;return false;}*lpUllContentLength = ullContentLength;return true;
}bool CWinHttpClient::_IsSupportAcceptRanges()
{WCHAR szBuf[MAX_PATH] = { 0 };DWORD dwBufferLength = sizeof(szBuf);DWORD dwIndex = 0;if (!_WinHttpQueryHeaders(WINHTTP_QUERY_ACCEPT_RANGES, WINHTTP_HEADER_NAME_BY_INDEX, &szBuf, &dwBufferLength, &dwIndex)){return false;}if (0 == _wcsicmp(szBuf, L"bytes")){return true;}return false;
}bool CWinHttpClient::_SetRequestDataRange(LONGLONG nBegin, LONGLONG nLength)
{WCHAR szBuf[MAX_PATH] = { 0 };if (0 == nLength){(void)::StringCchPrintfW(szBuf, _countof(szBuf), L"Range:bytes=%lld-%lld", nBegin, nBegin);}else if (nLength > 0){(void)::StringCchPrintfW(szBuf, _countof(szBuf), L"Range:bytes=%lld-%lld", nBegin, nBegin + nLength - 1);}else{(void)::StringCchPrintfW(szBuf, _countof(szBuf), L"Range:bytes=%lld-", nBegin);}return ::WinHttpAddRequestHeaders(m_hRequest, szBuf, (DWORD)wcslen(szBuf), 0);
}DWORD CWinHttpClient::_GetStatusCode(HINTERNET hRequest)
{DWORD dwInfoLevel = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;DWORD dwRespCode = 0;DWORD dwBufferLength = sizeof(dwRespCode);DWORD dwIndex = 0;if (!_WinHttpQueryHeaders(dwInfoLevel, WINHTTP_HEADER_NAME_BY_INDEX, &dwRespCode, &dwBufferLength, &dwIndex)){return dwRespCode;}return dwRespCode;
}bool CWinHttpClient::_InternetReadData(std::string& strData)
{HANDLE hFile = INVALID_HANDLE_VALUE;bool fResult = false;// 查询文件大小ULONGLONG ullContentLength = 0;if (!_QueryContentLength(&ullContentLength)){_PrintError(_T("QueryContentLength"));return false;}// 分配数据缓冲try{if (-1 != ullContentLength){strData.resize(ullContentLength, 0);}}catch (...){_PrintError(_T("std::string resize"));return false;}// 接收数据LPBYTE lpBufPos = (LPBYTE)strData.data();std::string strBuf;ULONGLONG ullDownloaded = 0;ULONGLONG ullTotalLength = ullContentLength;ULONGLONG ullLastDownload = 0;// 进度统计用变量clock_t refreshInterval = 1000;clock_t curTime = ::clock();clock_t startTime = curTime;clock_t lastTime = curTime;double lfRemainTime = 0.0f;double lfSpeed = 0.0f;if (!m_strFilePath.empty()){LARGE_INTEGER liDistanceToMove = { 0 };// 共享读写 创建/打开 文件, 多线程读写hFile = ::CreateFile(m_strFilePath.c_str(),GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL);if (INVALID_HANDLE_VALUE == hFile){_PrintError(_T("CreateFile"));return false;}// 设置数据写入位置liDistanceToMove.QuadPart = m_ullStart;::SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN);}do{// 检查可用数据DWORD dwNumberOfBytesAvailable = 0;DWORD dwNumberOfBytesRead = 0;if (!_WinHttpQueryDataAvailable(&dwNumberOfBytesAvailable)){_PrintError(_T("_WinHttpQueryDataAvailable"));break;}if (dwNumberOfBytesAvailable > 0){try{strBuf.resize(dwNumberOfBytesAvailable);}catch (...){_PrintError(_T("std::vector resize"));break;}if (!_WinHttpReadData(&strBuf[0], dwNumberOfBytesAvailable, &dwNumberOfBytesRead)){_PrintError(_T("_WinHttpReadData"));break;}// 写入到缓冲if (-1 == ullContentLength){strData += strBuf;ullDownloaded += strBuf.size();}else{memcpy(lpBufPos, &strBuf[0], dwNumberOfBytesRead);lpBufPos += dwNumberOfBytesRead;ullDownloaded += dwNumberOfBytesRead;}if (INVALID_HANDLE_VALUE != hFile){DWORD dwWritten = 0;if (!::WriteFile(hFile, strBuf.data(), dwNumberOfBytesRead, &dwWritten, NULL)){_PrintError(_T("WriteFile"));break;}}m_ullDownload += dwNumberOfBytesRead;}// 进度计算if (m_cbProgress){// 速度计算clock_t curTime = ::clock();if (curTime - lastTime >= refreshInterval  || 0 == dwNumberOfBytesAvailable){lfSpeed = (double)(ullDownloaded - ullLastDownload) / ((double)refreshInterval / 1000.0f);if (isinf(lfSpeed) || isnan(lfSpeed)){lfSpeed = 0.0f;}if (0 == dwNumberOfBytesAvailable){ullTotalLength = ullDownloaded;}// 进度报告{WINHTTP_PROGRESS_INFO progress = { 0 };progress.lfProgress = (double)ullDownloaded / (double)ullTotalLength;progress.ullCur = ullDownloaded;progress.ullTotal = ullTotalLength;progress.lfSpeed = lfSpeed;progress.costTime = curTime - startTime;progress.lfRemainTime = ((double)(ullTotalLength - ullDownloaded)) / lfSpeed;progress.nActiveThread = 1;progress.nTotalThread = 1;if (!m_cbProgress(progress)){break;}}lastTime = curTime;ullLastDownload = ullDownloaded;}}// 检查读取是否结束if (!dwNumberOfBytesAvailable){fResult = true;break;}} while (true);if (INVALID_HANDLE_VALUE != hFile){::CloseHandle(hFile);}return fResult;
}void CWinHttpClient::_PrintError(LPCTSTR lpszError) const
{return;ConsoleOutput(_T("[Error][LastError: %d Error: %d Result: %d][%s]\r\n"),::GetLastError(), m_AsyncData.AsyncResult.dwError,m_AsyncData.AsyncResult.dwResult,lpszError);
}void CWinHttpClient::ConsoleOutput(LPCTSTR pFormat, ...)
{size_t nCchCount = MAX_PATH;_tstring strResult(nCchCount, 0);va_list args;va_start(args, pFormat);do{//格式化输出字符串int nSize = _vsntprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);if (-1 != nSize){HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);::WriteConsole(console, strResult.c_str(), nSize, NULL, NULL);break;}//缓冲大小超限终止if (nCchCount >= INT32_MAX){break;}//重新分配缓冲nCchCount *= 2;strResult.resize(nCchCount);} while (true);va_end(args);
}#define WINHTTP_STATUS_TEXT(_code)  std::make_pair(_code, _T(#_code)),void CWinHttpClient::_PrintStatus(DWORD dwCode, LPVOID lpvStatusInfo, DWORD dwStatusInfoLength)
{std::map<DWORD, _tstring> mapWinHttpCode = {WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_RESOLVING_NAME             )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_NAME_RESOLVED              )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER       )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER        )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SENDING_REQUEST            )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_REQUEST_SENT               )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE         )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED          )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION         )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED          )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_HANDLE_CREATED             )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING             )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_DETECTING_PROXY            )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_REDIRECT                   )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE      )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SECURE_FAILURE             )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE          )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE             )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_READ_COMPLETE              )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE             )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_REQUEST_ERROR              )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE       )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE    )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE             )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE          )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_GETPROXYSETTINGS_COMPLETE  )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SETTINGS_WRITE_COMPLETE    )WINHTTP_STATUS_TEXT(WINHTTP_CALLBACK_STATUS_SETTINGS_READ_COMPLETE     )};auto itFind = mapWinHttpCode.find(dwCode);if (mapWinHttpCode.end() != itFind){ConsoleOutput(_T("%08X %-50s "), itFind->first, itFind->second.c_str());}if (dwCode == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER ||dwCode == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER ||dwCode == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED ||dwCode == WINHTTP_CALLBACK_STATUS_REDIRECT){if (NULL != lpvStatusInfo){LPWSTR lpData = (LPWSTR)lpvStatusInfo;ConsoleOutput(_T("Data %s"), WStrToTStr(lpData).c_str());}}if (dwCode == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE ||dwCode == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE ||dwCode == WINHTTP_CALLBACK_STATUS_REQUEST_SENT ||dwCode == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED ||dwCode == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE ||dwCode == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE){if (NULL != lpvStatusInfo){LPDWORD lpData = (LPDWORD)lpvStatusInfo;ConsoleOutput(_T("Data %d(%08X)"), *lpData, *lpData);}}if (dwCode == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR){if (NULL != lpvStatusInfo){LPWINHTTP_ASYNC_RESULT lpData = (LPWINHTTP_ASYNC_RESULT)lpvStatusInfo;ConsoleOutput(_T("Error %d(%08X) Result: %d(%08X)"), lpData->dwError, lpData->dwError,lpData->dwResult, lpData->dwResult);}}ConsoleOutput(_T("\r\n"));
}VOID CALLBACK CWinHttpClient::WinHttpStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength)
{CWinHttpClient* pThis = (CWinHttpClient*)dwContext;if (nullptr != pThis){pThis->_WinHttpStatusCallback(hInternet, dwStatus, lpvInfo, dwInfoLength);}
}// 状态回调
void CWinHttpClient::_WinHttpStatusCallback(HINTERNET hInternet, DWORD dwStatus, LPVOID lpvInfo, DWORD dwInfoLength)
{if (m_fPrint){_PrintStatus(dwStatus, lpvInfo, dwInfoLength);}switch (dwStatus){// 关闭与服务器的连接。 lpvStatusInformation 参数为 NULL。case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:{break;}// 已成功连接到服务器。 lpvStatusInformation 参数包含指向 LPWSTR 的指针,该指针以点表示法指示服务器的 IP 地址。case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:{LPWSTR lpData = (LPWSTR)lpvInfo;break;}// 连接到服务器。 lpvStatusInformation 参数包含指向 LPWSTR 的指针,该指针以点表示法指示服务器的 IP 地址。case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:{LPWSTR lpData = (LPWSTR)lpvInfo;break;}// 已成功关闭与服务器的连接。 lpvStatusInformation 参数为 NULL。case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:{break;}// 可以使用 WinHttpReadData 检索数据。 lpvStatusInformation 参数指向包含可用数据字节数的 DWORD。 // dwStatusInformationLength 参数本身是 4 (DWORD) 的大小。case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:// WinHttpQueryDataAvailable 完成{DWORD lpSize = *(LPDWORD)lpvInfo;m_AsyncData.dwSize = lpSize;m_AsyncData.AsyncResult.dwResult = 0;::SetEvent(m_AsyncData.hEvent);break;}// 已创建 HINTERNET 句柄。 lpvStatusInformation 参数包含指向 HINTERNET 句柄的指针。case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:{LPHINTERNET pHandle = (LPHINTERNET)lpvInfo;m_AsyncData.dwContext = (DWORD_PTR)*pHandle;::SetEvent(m_AsyncData.hEvent);break;}// 此句柄值已终止。 lpvStatusInformation 参数包含指向 HINTERNET 句柄的指针。 此句柄不再有回调。case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:{LPHINTERNET pHandle = (LPHINTERNET)lpvInfo;break;}// 响应标头已收到,可用于 WinHttpQueryHeaders。 lpvStatusInformation 参数为 NULL。case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: // WinHttpReceiveResponse 完成{m_AsyncData.AsyncResult.dwResult = 0;::SetEvent(m_AsyncData.hEvent);break;}// 从服务器收到中间 (100 级别) 状态代码消息。 lpvStatusInformation 参数包含指向指示状态代码的 DWORD 的指针。case WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE:{LPDWORD pStatusCode = (LPDWORD)lpvInfo;break;}// 已成功找到服务器的 IP 地址。 lpvStatusInformation 参数包含指向 LPWSTR 的指针,该指针指示已解析的名称。case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:{LPWSTR lpResolved = (LPWSTR)lpvInfo;break;}// 已成功从服务器读取数据。 // lpvStatusInformation 参数包含指向调用 WinHttpReadData 中指定的缓冲区的指针。 // dwStatusInformationLength 参数包含读取的字节数。// WinHttpWebSocketReceive 使用时,lpvStatusInformation 参数包含指向WINHTTP_WEB_SOCKET_STATUS结构的指针,// dwStatusInformationLength 参数指示 lpvStatusInformation 的大小。case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: // WinHttpReadData 完成{LPBYTE* ppBuf = (LPBYTE*)lpvInfo;DWORD dwRead = dwInfoLength;m_AsyncData.AsyncResult.dwResult = 0;m_AsyncData.dwSize = dwRead;::SetEvent(m_AsyncData.hEvent);break;}// 等待服务器响应请求。 lpvStatusInformation 参数为 NULL。case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:{break;}// HTTP 请求将自动重定向请求。 lpvStatusInformation 参数包含指向指示新 URL 的 LPWSTR 的指针。 // 此时,应用程序可以使用重定向响应读取服务器返回的任何数据,并且可以查询响应标头。 它还可以通过关闭句柄来取消操作。case WINHTTP_CALLBACK_STATUS_REDIRECT:{LPWSTR lpData = (LPWSTR)lpvInfo;break;}// 发送 HTTP 请求时出错。 lpvStatusInformation 参数包含指向WINHTTP_ASYNC_RESULT结构的指针。 // 其 dwResult 成员指示被调用函数的 ID,dwError 指示返回值。case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:{DWORD dwError = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;LPWINHTTP_ASYNC_RESULT pAsyncResult = (LPWINHTTP_ASYNC_RESULT)lpvInfo;m_AsyncData.AsyncResult = *pAsyncResult;::SetEvent(m_AsyncData.hEvent);break;}// 已成功将信息请求发送到服务器。 lpvStatusInformation 参数包含指向 DWORD 的指针,该指针指示发送的字节数。case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:{LPDWORD pSentBytes = (LPDWORD)lpvInfo;break;}// 查找服务器名称的 IP 地址。 lpvStatusInformation 参数包含指向要解析的服务器名称的指针。case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:{LPWSTR lpName = (LPWSTR)lpvInfo;break;}// 已成功从服务器收到响应。 lpvStatusInformation 参数包含指向指示接收字节数的 DWORD 的指针。case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:{LPDWORD pRecv = (LPDWORD)lpvInfo;break;}// 在与服务器建立安全 (HTTPS) 连接时遇到一个或多个错误。 // lpvStatusInformation 参数包含指向 DWORD 的指针,该指针是错误值的按位 OR 组合。 // 有关详细信息,请参阅 lpvStatusInformation 的说明。case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:{DWORD dwCode = *(LPDWORD)lpvInfo;switch (dwCode){// 证书吊销检查已启用,但吊销检查未能验证证书是否已吊销。 用于检查吊销的服务器可能无法访问case WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED:{break;}// SSL 证书无效case WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT:{break;}// SSL 证书已吊销case WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED:{break;}// 函数不熟悉生成服务器证书的证书颁发机构case WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA:{break;}// SSL 证书公用名 (主机名字段) 不正确,例如,如果输入 www.microsoft.com 且证书上的公用名显示 www.msn.comcase WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID:{break;}// 从服务器收到的 SSL 证书日期不正确。 证书已过期。case WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID:{break;}// 应用程序在加载 SSL 库时遇到内部错误case WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR:{break;}}break;}// 将信息请求发送到服务器。 lpvStatusInformation 参数为 NULL。case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:{break;}// 请求已成功完成。 // lpvStatusInformation 参数是传递给 WinHttpSendRequest (初始请求正文) 的 lpOptional 值,// dwStatusInformationLength 参数指示 (传递到 WinHttpSendRequest) 传递到 winHttpSendRequest 的 dwOptionalLength 值成功写入此类初始正文字节的数目。case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:  // WinHttpSendRequest 完成{m_AsyncData.AsyncResult.dwResult = 0;::SetEvent(m_AsyncData.hEvent);break;}// 数据已成功写入服务器。 lpvStatusInformation 参数包含指向 DWORD 的指针,该指针指示写入的字节数。// 当 WinHttpWebSocketSend 使用时, lpvStatusInformation 参数包含指向WINHTTP_WEB_SOCKET_STATUS结构的指针,// dwStatusInformationLength 参数指示 lpvStatusInformation 的大小。case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:    // WinHttpWriteData 完成{m_AsyncData.AsyncResult.dwResult = 0;::SetEvent(m_AsyncData.hEvent);break;}// 通过调用 WinHttpGetProxyForUrlEx 启动的操作已完成。 可以使用 WinHttpReadData 检索数据。case WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE:{break;}// 通过调用 WinHttpWebSocketClose 成功关闭了连接。 lpvStatusInformation 参数为 NULL。case WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE:{break;}// 通过调用 WinHttpWebSocketShutdown 成功关闭连接。 lpvStatusInformation 参数为 NULL。case WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE:{break;}}
}bool CWinHttpClient::_WinHttpOpen(LPCWSTR pszAgentW, DWORD dwAccessType, LPCWSTR pszProxyW, LPCWSTR pszProxyBypassW, DWORD dwFlags)
{// 初始化 WinHTTP 函数的使用并返回 WinHTTP 会话句柄。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpopenm_hSession = ::WinHttpOpen(pszAgentW,                      // 指向字符串变量的指针,此名称用作 HTTP 协议中的 用户代理dwAccessType,                   // 所需的访问类型pszProxyW,                      // 代理访问时要使用的代理服务器的名称pszProxyBypassW,                // 代理访问时要使用的代理服务器的密码dwFlags                         // 指示影响此函数行为的各种选项的标志);return NULL != m_hSession;
}bool CWinHttpClient::_WinHttpConnect(LPCWSTR pswzServerName, INTERNET_PORT nServerPort)
{// 指定 HTTP 请求的初始目标服务器,并将 HINTERNET 连接句柄返回到该初始目标的 HTTP 会话// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpconnect// 即使 WinHTTP 在异步模式下使用(即在 WinHttpOpen中设置 WINHTTP_FLAG_ASYNC),此函数也会同步运行m_hConnect = ::WinHttpConnect(m_hSession,                   // 由先前调用 WinHttpOpen 返回的有效 HINTERNETWinHTTP 会话句柄pswzServerName,             // HTTP 服务器的主机名nServerPort,                // 建立连接的服务器上的 TCP/IP 端口0                           // 保留参数, 必须为0);return NULL != m_hConnect;
}bool CWinHttpClient::_WinHttpOpenRequest(const _tstring& strVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags)
{// 创建 HTTP 请求句柄// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpopenrequestHINTERNET hRequest = ::WinHttpOpenRequest(m_hConnect,                         // WinHttpConnect 返回的 HTTP 会话的 HINTERNET 连接句柄TStrToWStr(strVerb).c_str(),        // 请求的 HTTP 谓词pwszObjectName,                     // 指定 HTTP 谓词的目标资源名称pwszVersion,                        // HTTP 版本的字符串的指针pwszReferrer,                       // 指定从中获取 请求 pwszObjectName 中的 URL 的文档的 URLppwszAcceptTypes,                   // 指定客户端接受的媒体类型dwFlags                             // Internet 标志值);// 等待异步请求完成if (!_WaitForAsyncEvent()){return NULL;}m_hRequest = (HINTERNET)m_AsyncData.dwContext;return NULL != m_hRequest;
}bool CWinHttpClient::_WinHttpSetSessionTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout)
{// 设置与 HTTP 事务相关的超时。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsettimeouts// 即使在异步模式下使用 WinHTTP (即在 WinHttpOpen) 中设置了WINHTTP_FLAG_ASYNC时,此函数也会同步运行。return ::WinHttpSetTimeouts(m_hSession,             // WinHttpOpen 或 WinHttpOpenRequest 返回的 HINTERNET 句柄。nResolveTimeout,        // 名称解析的超时值(以毫秒为单位)nConnectTimeout,        // 服务器连接请求的超时值(以毫秒为单位)nSendTimeout,           // 发送请求的超时值(以毫秒为单位)nReceiveTimeout         // 接收对请求的响应超超时值(以毫秒为单位));
}bool CWinHttpClient::_WinHttpSetRequestTimeouts(int nResolveTimeout, int nConnectTimeout,int nSendTimeout,int nReceiveTimeout)
{// 设置与 HTTP 事务相关的超时。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsettimeouts// 即使在异步模式下使用 WinHTTP (即在 WinHttpOpen) 中设置了WINHTTP_FLAG_ASYNC时,此函数也会同步运行。return ::WinHttpSetTimeouts(m_hRequest,             // WinHttpOpen 或 WinHttpOpenRequest 返回的 HINTERNET 句柄。nResolveTimeout,        // 名称解析的超时值(以毫秒为单位)nConnectTimeout,        // 服务器连接请求的超时值(以毫秒为单位)nSendTimeout,           // 发送请求的超时值(以毫秒为单位)nReceiveTimeout         // 接收对请求的响应超超时值(以毫秒为单位));
}bool CWinHttpClient::_WinHttpSetStatusCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags
)
{// 设置回调函数// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetstatuscallbackWINHTTP_STATUS_CALLBACK statusCallback = ::WinHttpSetStatusCallback(m_hSession,                 // 要为其设置回调的 HINTERNET 句柄lpfnInternetCallback,       // 指向进度时要调用的回调函数的指针dwNotificationFlags,        // 无符号长整数值,该值指定标志以指示哪些事件激活回调函数0);if (WINHTTP_INVALID_STATUS_CALLBACK == statusCallback){return false;}return true;
}bool CWinHttpClient::_WaitForAsyncEvent(DWORD dwMilliseconds/* = INFINITE*/)
{// 等待异步事件响应m_AsyncData.dwWait = ::WaitForSingleObject(m_AsyncData.hEvent, dwMilliseconds);if (m_fAbort){return false;}if (WAIT_OBJECT_0 == m_AsyncData.dwWait && 0 == m_AsyncData.AsyncResult.dwResult){return true;}return false;
}bool CWinHttpClient::_WinHttpSetSessionOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
{// 设置 Internet 选项。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetoptionreturn ::WinHttpSetOption(m_hSession, dwOption, lpBuffer, dwBufferLength);
}bool CWinHttpClient::_WinHttpSetRequestOption(DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength)
{// 设置 Internet 选项。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetoptionreturn ::WinHttpSetOption(m_hRequest, dwOption, lpBuffer, dwBufferLength);
}bool CWinHttpClient::_WinHttpSendRequest(_tstring strHeader, LPVOID lpData, DWORD dwSize, DWORD_PTR dwContext)
{std::wstring wstrHeader = TStrToWStr(strHeader);LPCWSTR lpHeader = (LPCWSTR)wstrHeader.data();DWORD dwHeaderSize = (DWORD)wstrHeader.size();if (wstrHeader.empty()){lpHeader = WINHTTP_NO_ADDITIONAL_HEADERS;dwHeaderSize = 0;}bool fResult = false;do{// 发送请求// 将指定的请求发送到 HTTP 服务器。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsendrequestif (::WinHttpSendRequest(m_hRequest,             //WinHttpOpenRequest 返回的 HINTERNET 句柄lpHeader,               //要追加到请求的其他标头dwHeaderSize,           //附加标头的长度(以字符为单位)lpData,                 //请求标头之后发送的任何可选数据dwSize,                 //可选数据的长度(以字节为单位)dwSize,                 //发送的总数据的长度dwContext               //上下文)){fResult = true;break;}// 安全 HTTP 服务器需要客户端证书if (ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED != ::GetLastError()){break;}// 设置 Internet 选项// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsetoptionif (!_WinHttpSetRequestOption(WINHTTP_OPTION_CLIENT_CERT_CONTEXT,WINHTTP_NO_CLIENT_CERT_CONTEXT,0)){break;}// 再次发送请求// 将指定的请求发送到 HTTP 服务器。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpsendrequestif (!::WinHttpSendRequest(m_hRequest,             // WinHttpOpenRequest 返回的 HINTERNET 句柄lpHeader,               // 要追加到请求的其他标头dwHeaderSize,           // 附加标头的长度(以字符为单位)lpData,                 // 请求标头之后发送的任何可选数据dwSize,                 // 可选数据的长度(以字节为单位)dwSize,                 // 发送的总数据的长度NULL)){break;}fResult = true;} while (false);if (!fResult){return false;}// 等待异步请求完成return _WaitForAsyncEvent();
}bool CWinHttpClient::_WinHttpReceiveResponse(HINTERNET hRequest)
{// 等待接收 WinHttpSendRequest 发起的 HTTP 请求的响应。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpreceiveresponseif (!::WinHttpReceiveResponse(hRequest,       // WINHttpOpenRequest 返回并由 WinHttpSendRequest 发送的 HINTERNET 句柄。NULL            // 此参数是保留的,必须为 NULL。)){return false;}// 等待异步请求完成return _WaitForAsyncEvent();
}bool CWinHttpClient::_WinHttpQueryHeaders(DWORD dwInfoLevel, LPCWSTR pwszName, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
{return ::WinHttpQueryHeaders(m_hRequest,                     //WinHttpOpenRequest 返回的 HINTERNET 请求句柄dwInfoLevel,                    //指定“查询信息标志”页上列出的属性标志和修饰符标志的组合pwszName,                       //标头名称lpBuffer,                       //接收信息的缓冲区lpdwBufferLength,               //数据缓冲区的长度lpdwIndex                       //从零开始的标头索引的指针,用于枚举具有相同名称的多个标头);
}bool CWinHttpClient::_WinHttpQueryDataAvailable(LPDWORD lpdwNumberOfBytesAvailable)
{m_AsyncData.dwSize = 0;// 返回可使用 WinHttpReadData 读取的数据量(以字节为单位)。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpquerydataavailableif (!::WinHttpQueryDataAvailable(m_hRequest,                     // WinHttpOpenRequest 返回的有效 HINTERNET 句柄。 // WinHttpReceiveResponse 必须已为此句柄调用,并在调用 WinHttpQueryDataAvailable 之前完成。NULL                            // 指向接收可用字节数的无符号长整数变量的指针。 // 在异步模式下使用 WinHTTP 时,始终将此参数设置为 NULL ,并在回调函数中检索数据;// 不这样做可能会导致内存故障。)){return false;}// 等待异步请求完成if (!_WaitForAsyncEvent()){return false;}*lpdwNumberOfBytesAvailable = m_AsyncData.dwSize;return true;
}bool CWinHttpClient::_WinHttpReadData(LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead)
{m_AsyncData.dwSize = 0;// 从 WinHttpOpenRequest 函数打开的句柄读取数据。// https://learn.microsoft.com/zh-cn/windows/win32/api/winhttp/nf-winhttp-winhttpreaddataif (!::WinHttpReadData(m_hRequest,             // 从上一次调用 WinHttpOpenRequest 返回的有效 HINTERNET 句柄。lpBuffer,               // 指向接收读取数据的缓冲区的指针。 确保此缓冲区在 WinHttpReadData 完成之前保持有效。dwNumberOfBytesToRead,  // 要读取的字节数的无符号长整数值。NULL                    // 指向接收读取字节数的无符号长整数变量的指针。 // WinHttpReadData 在执行任何工作或错误检查之前将此值设置为零。 // 异步使用 WinHTTP 时,始终将此参数设置为 NULL ,并在回调函数中检索信息;// 不这样做可能会导致内存故障。)){return false;}// 等待异步请求完成if (!_WaitForAsyncEvent()){return false;}*lpdwNumberOfBytesRead = m_AsyncData.dwSize;return true;
}

main.cpp

#include <iostream>
#include "CWinHttpClient.h"#define UPDATE_URL1          _T("https://gitee.com/flame_cyclone/fc_font_tool/raw/master/Release/update.json")
#define UPDATE_URL2          _T("https://gitee.com/flame_cyclone/fc_font_tool/releases/download/1.0.0.4/FC_Font_Tool.exe")
#define TEST_NVIDIA_DRIVER   _T("https://cn.download.nvidia.com/Windows/561.09/561.09-desktop-win10-win11-64bit-international-dch-whql.exe")
#define TEST_AMD_DRIVER      _T("https://drivers.amd.com/drivers/whql-amd-software-adrenalin-edition-24.8.1-win10-win11-aug-rdna.exe")
#define TEST_BILIBILI_DM     _T("https://api.live.bilibili.com/xlive/web-room/v1/dM/gethistory?roomid=4412054")
#define TEST_WEPE_URL        _T("https://mirrors.lzu.edu.cn/wepe/WePE_64_V2.3.exe")int main()
{CWinHttpClient obj;obj.AddRequestHeader(_T("Content-Type"), _T("application/json;charset=UTF-8"));obj.AddRequestHeader(_T("Accept"), _T("*/*"));obj.AddRequestHeader(_T("Referer"), _T("https://www.amd.com/"));obj.SetAgent();bool fResult;clock_t startTime = ::clock();for (int i = 0; i < 1; i++){fResult = obj.DownloadFile(TEST_AMD_DRIVER, _T(""), [](const WINHTTP_PROGRESS_INFO& progress) {CWinHttpClient::ConsoleOutput(_T("%d/%d Time: %.3lfs Progress: %.3lf%% %.1lfKB/%.1lfKB Speed: %.1lf Mbps %.1lfMB/s %.1lfKB/s %.1lfB/s RemainTime: %.1lfs\n"),progress.nActiveThread, progress.nTotalThread,(double)progress.costTime / 1000.0f,progress.lfProgress * 100, (double)progress.ullCur / (1024.0f), (double)progress.ullTotal / (1024.0f),progress.lfSpeed / (1024.0f * 1024.0f) * 8.0f,progress.lfSpeed / (1024.0f * 1024.0f),progress.lfSpeed / (1024.0f), progress.lfSpeed, progress.lfRemainTime);return true;}, 4);}clock_t endTime = ::clock();CWinHttpClient::ConsoleOutput(_T("Cost Time: %dms\r\n"), endTime - startTime);return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com