内容目录
- —— 引言
- —— WinHttp类设计
- —— 多线程文件下载
- —— 结语
引言
在Windows平台上进行网络编程时,微软提供了WinHttp API供开发者使用。WinHttp API允许开发者发送HTTP请求,接收响应,并支持HTTPS协议。本文将介绍如何使用WinHttp API封装一个类,实现GET和POST请求,以及支持多线程文件下载的功能。
WinHttp类设计
我们将创建一个名为WinHttpClient
的类,该类将封装WinHttp API的关键功能,并提供易于使用的接口。
1. 类定义
#include <winhttp.h>
#include <string>
#include <vector>
#include <memory>
class WinHttpClient {
public:
WinHttpClient();
~WinHttpClient();
bool Initialize();
void Uninitialize();
std::string SendGetRequest(const std::string& url);
std::string SendPostRequest(const std::string& url, const std::string& postData);
private:
HINTERNET _session;
};
2. 初始化与销毁
在使用WinHttpClient
类之前,我们需要先初始化会话,使用完毕后释放资源。
WinHttpClient::WinHttpClient() : _session(nullptr) {}
WinHttpClient::~WinHttpClient() {
Uninitialize();
}
bool WinHttpClient::Initialize() {
_session = WinHttpOpen(L"WinHttpClient Sample",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
return _session != nullptr;
}
void WinHttpClient::Uninitialize() {
if (_session) {
WinHttpCloseHandle(_session);
_session = nullptr;
}
}
3. 发送GET请求
发送GET请求相对简单,只需要创建连接,打开请求,并接收响应即可。
std::string WinHttpClient::SendGetRequest(const std::string& url) {
if (!_session) {
return "";
}
HINTERNET connection = WinHttpConnect(_session, url.c_str(), INTERNET_DEFAULT_HTTP_PORT, 0);
if (!connection) {
return "";
}
HINTERNET request = WinHttpOpenRequest(connection, L"GET", url.c_str(), nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!request) {
WinHttpCloseHandle(connection);
return "";
}
if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, nullptr, 0, 0, 0)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return "";
}
if (!WinHttpReceiveResponse(request, nullptr)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return "";
}
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
if (!WinHttpQueryHeaders(request, WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return "";
}
char buffer[4096];
DWORD bytesRead = 0;
std::string response;
while (WinHttpReadData(request, reinterpret_cast<LPVOID>(buffer), sizeof(buffer) - 1, &bytesRead)) {
buffer[bytesRead] = '\0';
response.append(buffer, bytesRead);
}
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return response;
}
4. 发送POST请求
发送POST请求时,除了需要发送数据外,还需要设置正确的请求头。
std::string WinHttpClient::SendPostRequest(const std::string& url, const std::string& postData) {
if (!_session) {
return "";
}
HINTERNET connection = WinHttpConnect(_session, url.c_str(), INTERNET_DEFAULT_HTTP_PORT, 0);
if (!connection) {
return "";
}
HINTERNET request = WinHttpOpenRequest(connection, L"POST", url.c_str(), nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
if (!request) {
WinHttpCloseHandle(connection);
return "";
}
if (!WinHttpAddRequestHeaders(request, L"Content-Type: application/x-www-form-urlencoded", -1, WINHTTP_ADDREQ_FLAG_ADD)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return "";
}
DWORD bytesWritten = 0;
if (!WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, reinterpret_cast<const BYTE*>(postData.c_str()), static_cast<DWORD>(postData.size()), 0, 0)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return "";
}
if (!WinHttpReceiveResponse(request, nullptr)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return "";
}
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
if (!WinHttpQueryHeaders(request, WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return "";
}
char buffer[4096];
DWORD bytesRead = 0;
std::string response;
while (WinHttpReadData(request, reinterpret_cast<LPVOID>(buffer), sizeof(buffer) - 1, &bytesRead)) {
buffer[bytesRead] = '\0';
response.append(buffer, bytesRead);
}
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
return response;
}
多线程文件下载
为了实现多线程文件下载,我们需要扩展上述类的功能,加入对分段下载的支持。
5. 多线程下载
这里我们简要介绍如何实现多线程下载的思路。具体实现细节可能需要根据实际需求定制。
// 假设定义了一个下载任务类
class DownloadTask {
public:
void operator()(const std::string& url, const std::string& filePath, DWORD startOffset, DWORD endOffset);
};
// 下载任务执行
void DownloadTask::operator()(const std::string& url, const std::string& filePath, DWORD startOffset, DWORD endOffset) {
// 使用WinHttpClient发送带有Range头的GET请求
// 接收响应并将数据写入文件的相应位置
}
6. 主线程调度
在主线程中,我们需要计算文件分块,并为每个分块创建并启动一个下载线程。
#include <thread>
#include <vector>
int main() {
// 初始化WinHttpClient
WinHttpClient client;
if (!client.Initialize()) {
return -1;
}
// 获取文件大小
DWORD fileSize = GetFileSizeFromUrl(url);
DWORD chunkSize = 1024 * 1024; // 每个分块1MB
// 创建任务向量
std::vector<std::thread> threads;
for (DWORD i = 0; i < fileSize; i += chunkSize) {
DWORD endOffset = std::min(i + chunkSize - 1, fileSize - 1);
threads.emplace_back(DownloadTask(), url, filePath, i, endOffset);
}
// 等待所有线程完成
for (auto& thread : threads) {
thread.join();
}
// 清理
client.Uninitialize();
return 0;
}
结语
通过上述步骤,我们封装了一个基于WinHttp API的客户端类,支持GET和POST请求,并且能够实现多线程文件下载。希望这篇教程能够帮助您更好地理解和使用WinHttp API进行网络编程。
本文介绍了如何使用WinHttp API封装一个支持GET、POST请求及多线程文件下载的类,并提供了完整的代码示例。希望对您在Windows平台上进行网络编程时有所帮助。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END