初始化
InternetOpen
HINTERNET InternetOpen(
LPCSTR lpszAgent,
DWORD dwAccessType,
LPCSTR lpszProxyName,
LPCSTR lpszProxyBypass,
DWORD dwFlags
);
"InternetOpen"是Windows操作系统提供的函数之一,位于WinINet库中。它用于初始化WinINet网络会话,并返回一个用于后续网络操作的句柄。
参数说明:
lpszAgent
: 一个指向字符串的指针,表示应用程序名称或标识符,用于标识创建的会话。- dwAccessType: 指定访问类型,可以是以下几个值之一:
- INTERNET_OPEN_TYPE_PRECONFIG`: 使用系统配置的代理设置。
INTERNET_OPEN_TYPE_DIRECT
: 直接连接到互联网。INTERNET_OPEN_TYPE_PROXY
: 使用指定的代理设置。INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY
: 使用系统配置的代理设置,但不自动检测自动代理设置。
lpszProxyName
: 一个指向字符串的指针,表示使用的代理服务器名称。lpszProxyBypass
: 一个指向字符串的指针,表示不使用代理的地址。- dwFlags:一些选项标志,可以是0或以下值的组合:
INTERNET_FLAG_ASYNC
: 异步模式,表示启用异步操作。在异步模式下,函数将立即返回而不阻塞当前线程,并通过回调函数来处理请求的结果和状态。INTERNET_FLAG_FROM_CACHE
: 如果可用,从缓存中获取数据。INTERNET_FLAG_OFFLINE
: 无网络连接时,从缓存中获取数据。
返回值:
- 成功时,返回一个有效的HINTERNET句柄,用于后续的网络操作。
- 失败时,返回NULL,可以通过调用
GetLastError()
获取错误代码。
使用InternetOpen
函数是进行网络操作的第一步,它初始化了一个网络会话,然后可以通过后续的WinINet函数来进行HTTP请求、FTP传输、下载文件等网络操作。
InternetSetStatusCallback
InternetSetStatusCallback
是Windows操作系统提供的函数之一,位于WinINet库中。它用于为异步的WinINet操作设置回调函数,以便在操作状态发生变化时接收通知。
INTERNET_STATUS_CALLBACK InternetSetStatusCallback(
HINTERNET hInternet,
INTERNET_STATUS_CALLBACK lpfnInternetCallback
);
参数说明:
-
hInternet: 要设置回调函数的网络会话或请求句柄。
-
lpfnInternetCallback: 回调函数指针,指向用于接收回调通知的函数。回调函数必须符合INTERNET_STATUS_CALLBACK类型的定义。
- 回调函数的定义如下:
void CALLBACK InternetStatusCallback( HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength );
参数说明:
-
hInternet: 引发状态回调的网络会话或请求句柄。
-
dwContext: 在调用InternetSetStatusCallback时传递的上下文参数。
-
dwInternetStatus: 表示引发回调的状态类型,具体取决于WinINet操作的不同阶段和情况。
-
lpvStatusInformation: 指向一个包含有关状态的信息的缓冲区。
-
dwStatusInformationLength: 缓冲区的大小。
-
回调函数的主要作用是接收WinINet异步操作的状态变化通知,例如请求的进度、连接状态、错误信息等。在设置了回调函数后,当相应的操作状态发生变化时,系统会自动调用回调函数并传递相关的状态信息。
通过设置回调函数,可以在异步操作中实现非阻塞的网络请求,提高程序的并发性和响应性。回调函数中可以根据不同的状态类型,采取相应的操作,比如处理请求结果、更新进度、处理错误等。
需要注意的是,使用异步操作时,需要仔细处理回调函数的调用时机和回调数据的有效性,以确保程序的正确性和稳定性。
CRITICAL_SECTION
CRITICAL_SECTION
是 Windows API 中用于创建临界区(Critical Section)的数据结构。临界区是一种用于线程同步的机制,它可以确保在多线程环境下,只有一个线程可以进入被锁定的临界区代码段,从而实现对共享资源的互斥访问。
CRITICAL_SECTION
是一个临界区对象,使用前需要进行初始化,可以通过以下两种方式进行初始化:
- 使用
InitializeCriticalSection
函数进行初始化:
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
- 使用
InitializeCriticalSectionAndSpinCount
函数进行初始化,并指定自旋次数:
CRITICAL_SECTION cs;
InitializeCriticalSectionAndSpinCount(&cs, 4000); // 指定自旋次数,比如4000
CreateEvent
CreateEvent
是 Windows API 中用于创建一个事件对象的函数。事件对象是用于线程间通信和同步的一种内核对象,它可以用于多个线程之间的同步或线程的等待操作。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
参数说明:
lpEventAttributes
: 一个指向SECURITY_ATTRIBUTES
结构的指针,用于指定事件对象的安全属性。可以为NULL,表示使用默认安全属性。bManualReset
: 一个BOOL值,用于指定事件是否为手动重置。如果为TRUE,则事件需要手动调用ResetEvent
函数来重置为非信号状态;如果为FALSE,则事件会在有一个线程等待它时自动重置为非信号状态。bInitialState
: 一个BOOL值,用于指定事件的初始状态。如果为TRUE,则事件初始为有信号状态;如果为FALSE,则事件初始为无信号状态。lpName
: 一个指向以NULL结尾的字符串的指针,用于指定事件的名称。可以为NULL,表示事件没有名称。
CreateEvent
函数创建一个事件对象并返回一个句柄,可以用于后续操作。通常情况下,可以通过 SetEvent
函数将事件设置为有信号状态,通过 ResetEvent
函数将事件设置为无信号状态。线程可以调用 WaitForSingleObject
或 WaitForMultipleObjects
函数来等待事件对象。
需要注意的是,当不再使用事件对象时,应该通过 CloseHandle
函数关闭句柄,以释放资源:
CloseHandle(hEvent);
InternetConnect
InternetConnect
是 Windows API 中的一个函数,位于 WinINet 库中,用于在客户端应用程序中建立到远程服务器的连接。
HINTERNET InternetConnect(
HINTERNET hInternet,
LPCTSTR lpszServerName,
INTERNET_PORT nServerPort,
LPCTSTR lpszUserName,
LPCTSTR lpszPassword,
DWORD dwService,
DWORD dwFlags,
DWORD_PTR dwContext
);
参数说明:
hInternet
: 网络会话句柄,通常是通过InternetOpen
函数返回的。lpszServerName
: 服务器的名称或 IP 地址,可以是一个以 NULL 结尾的字符串。nServerPort
: 服务器的端口号。lpszUserName
: 连接服务器所需的用户名,可以为 NULL。lpszPassword
: 连接服务器所需的密码,可以为 NULL。dwService
: 服务类型,通常使用INTERNET_SERVICE_HTTP
或INTERNET_SERVICE_FTP
来指定 HTTP 或 FTP 服务。dwFlags
: 连接选项标志,可以使用INTERNET_FLAG_PASSIVE
等来指定连接选项。dwContext
: 用户定义的上下文参数,将在回调函数中使用。
InternetConnect
函数用于建立与远程服务器的连接。它会返回一个连接句柄,用于后续的网络操作,如发送请求、下载数据等。
需要注意的是,在使用 WinINet API 进行网络操作时,需要先调用 InternetOpen
函数打开一个网络会话,再通过 InternetConnect
函数建立连接,最后使用其他相关函数来发送请求和接收响应。
同时,在使用完网络操作后,需要通过 InternetCloseHandle
函数关闭相关的句柄,以释放资源和确保程序的稳定性。
HttpOpenRequest
HttpOpenRequest
是 Windows API 中的一个函数,位于 WinINet 库中,用于创建一个 HTTP 请求句柄,以发送 HTTP 请求并接收服务器的响应。
HINTERNET HttpOpenRequest(
HINTERNET hConnect,
LPCTSTR lpszVerb,
LPCTSTR lpszObjectName,
LPCTSTR lpszVersion,
LPCTSTR lpszReferrer,
LPCTSTR *lplpszAcceptTypes,
DWORD dwFlags,
DWORD_PTR dwContext
);
参数说明:
hConnect
: 已建立的连接句柄,通常是通过InternetConnect
函数返回的。lpszVerb
: HTTP 请求的动作,例如 “GET”、“POST” 等。lpszObjectName
: 请求的对象,通常是请求的资源的路径。lpszVersion
: HTTP 协议的版本,例如 “HTTP/1.1”。lpszReferrer
: 引用页,通常是一个前一个页面的 URL。lplpszAcceptTypes
: 可接受的 MIME 类型数组。dwFlags
: 请求选项标志,可以使用INTERNET_FLAG_SECURE
等来指定请求选项。dwContext
: 用户定义的上下文参数,将在回调函数中使用。
HttpOpenRequest
函数用于创建一个 HTTP 请求句柄,并返回该句柄用于后续的网络操作,如发送请求、接收响应等。
需要注意的是,在使用 WinINet API 进行 HTTP 请求时,需要先打开一个网络会话,然后建立连接,再创建请求句柄,最后使用其他相关函数来发送请求和接收响应。
同时,在使用完 HTTP 请求后,需要通过 InternetCloseHandle
函数关闭相关的句柄,以释放资源和确保程序的稳定性。
EnterCriticalSection
EnterCriticalSection
是 Windows API 中的一个函数,用于进入一个临界区(Critical Section)。临界区是一种同步机制,用于保护共享资源,防止多个线程同时访问和修改共享资源而导致数据不一致或竞争条件。
void EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
参数 lpCriticalSection
是一个指向临界区对象的指针,通常是 CRITICAL_SECTION
类型的指针。临界区对象需要先进行初始化,可以使用 InitializeCriticalSection
函数来初始化。
使用方法示例:
CRITICAL_SECTION cs; // 定义一个临界区对象
InitializeCriticalSection(&cs); // 初始化临界区对象
// 在需要保护共享资源的地方,进入临界区
EnterCriticalSection(&cs);
// 访问和修改共享资源的代码
// ...
// 离开临界区
LeaveCriticalSection(&cs);
InternetCloseHandle
InternetCloseHandle
是 Windows API 中的一个函数,位于 WinINet 库中,用于关闭由 WinINet API 创建的句柄。
BOOL InternetCloseHandle(
HINTERNET hInternet
);
参数 hInternet
是需要关闭的句柄。该句柄可以是通过 InternetOpen
、InternetConnect
、HttpOpenRequest
等函数创建的,用于进行网络操作。
调用 InternetCloseHandle
函数后,系统会释放相应的资源,并将句柄置为无效。
WaitForSingleObject
WaitForSingleObject
是 Windows API 中的一个函数,用于等待一个对象(如线程、进程、事件等)进入有信号状态或超时。该函数是用于多线程编程中的同步机制。
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数说明:
hHandle
: 要等待的对象的句柄。dwMilliseconds
: 等待的超时时间,以毫秒为单位。如果设置为 INFINITE,表示无限等待,直到对象进入有信号状态。
WaitForSingleObject
函数将等待指定的对象进入有信号状态,即该对象完成了某个操作或事件发生。当对象进入有信号状态时,函数返回 WAIT_OBJECT_0
,表示成功等待到了对象的信号。如果在指定的超时时间内对象没有进入有信号状态,则函数返回 WAIT_TIMEOUT
。
HttpSendRequest
HttpSendRequest
是 Windows API 中的一个函数,位于 WinINet 库中,用于向服务器发送 HTTP 请求。
BOOL HttpSendRequest(
HINTERNET hRequest,
LPCWSTR lpszHeaders,
DWORD dwHeadersLength,
LPVOID lpOptional,
DWORD dwOptionalLength
);
参数说明:
hRequest
: HTTP 请求句柄,通过调用HttpOpenRequest
函数返回。lpszHeaders
: 指向包含请求头的字符串指针,可以为 NULL。dwHeadersLength
: 请求头字符串的长度,如果lpszHeaders
为 NULL,则该参数应该为 0。lpOptional
: 指向包含请求体的缓冲区指针,可以为 NULL。dwOptionalLength
: 请求体缓冲区的大小,如果lpOptional
为 NULL,则该参数应该为 0。
HttpSendRequestEx
HttpSendRequestEx
是 Windows API 中的一个函数,位于 WinINet 库中,用于向服务器发送 HTTP 请求并接收服务器的响应。它与 HttpSendRequest
类似,但提供了更多的控制和扩展选项。
BOOL HttpSendRequestEx(
HINTERNET hRequest,
LPINTERNET_BUFFERS lpBuffersIn,
LPINTERNET_BUFFERS lpBuffersOut,
DWORD dwFlags,
DWORD_PTR dwContext
);
HttpSendRequestEx
是 Windows API 中的一个函数,位于 WinINet 库中,用于向服务器发送 HTTP 请求并接收服务器的响应。它与 HttpSendRequest
类似,但提供了更多的控制和扩展选项。
函数原型如下:
cppCopy codeBOOL HttpSendRequestEx(
HINTERNET hRequest,
LPINTERNET_BUFFERS lpBuffersIn,
LPINTERNET_BUFFERS lpBuffersOut,
DWORD dwFlags,
DWORD_PTR dwContext
);
参数说明:
-
hRequest
: HTTP 请求句柄,通过调用HttpOpenRequest
函数返回。 -
lpBuffersIn
: 指向一个INTERNET_BUFFERS
结构体的指针,用于设置请求数据的缓冲区。 -
lpBuffersOut
: 指向一个INTERNET_BUFFERS
结构体的指针,用于接收服务器响应数据的缓冲区。 -
dwFlags
: 指定请求的标志,可以为以下常量的组合:
INTERNET_FLAG_RELOAD
: 强制从服务器获取资源,忽略本地缓存。INTERNET_FLAG_NO_CACHE_WRITE
: 不将资源添加到本地缓存。INTERNET_FLAG_FORMS_SUBMIT
: 将请求用于 HTML 表单的提交。- 其他标志,具体可参考官方文档。
-
dwContext
: 用户定义的上下文参数,将在异步操作时传递给回调函数。
HttpSendRequestEx
函数用于向服务器发送 HTTP 请求,并接收服务器的响应。通过 lpBuffersIn
和 lpBuffersOut
参数,可以设置请求的数据和接收服务器的响应数据。在调用该函数后,可以使用 InternetReadFile
函数来读取服务器返回的数据。
使用方法示例:
HINTERNET hInternet = InternetOpen(...); // 打开一个 Internet 会话
HINTERNET hConnect = InternetConnect(hInternet, ...); // 建立到服务器的连接
HINTERNET hRequest = HttpOpenRequest(hConnect, ...); // 创建一个 HTTP 请求句柄
INTERNET_BUFFERS buffersIn = { 0 }; // 设置请求数据的缓冲区
// 在 buffersIn 中设置请求数据的相关信息
INTERNET_BUFFERS buffersOut = { 0 }; // 接收服务器响应数据的缓冲区
// 在 buffersOut 中设置接收数据的相关信息
// 发送 HTTP 请求并接收服务器的响应
BOOL bSend = HttpSendRequestEx(hRequest, &buffersIn, &buffersOut, dwFlags, dwContext);
if (bSend) {
// 请求发送成功并接收到服务器的响应,可以继续处理服务器响应数据
// 使用 InternetReadFile 函数读取服务器返回的数据
} else {
// 请求发送失败,可以根据 GetLastError 获取错误代码进行相应的处理
}
// 关闭句柄,释放资源
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
ReadFile
ReadFile
是 Windows API 中的一个函数,位于 kernel32.dll 中,用于从文件、管道、字符设备等对象中读取数据。
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
参数说明:
hFile
: 要读取的文件、管道或设备的句柄。lpBuffer
: 指向用于存储读取数据的缓冲区。nNumberOfBytesToRead
: 要读取的字节数。lpNumberOfBytesRead
: 指向一个变量,用于接收实际读取的字节数。lpOverlapped
: 用于异步操作的OVERLAPPED
结构体指针,可设置为 NULL 表示使用同步操作。
ReadFile
函数用于从指定的文件、管道或设备中读取数据,并将读取的数据存储到指定的缓冲区中。如果读取成功,则返回 TRUE,并通过 lpNumberOfBytesRead
参数返回实际读取的字节数。如果读取失败,则返回 FALSE,并可以通过调用 GetLastError
函数获取错误代码。
使用方法示例:
#include <Windows.h>
int main() {
HANDLE hFile = CreateFile(L"C:\\path\\to\\file.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
// 处理文件打开失败的情况
return -1;
}
char buffer[1024];
DWORD bytesRead = 0;
if (ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL)) {
// 读取成功,bytesRead 中存储了实际读取的字节数
// 可以对 buffer 中的数据进行处理
} else {
// 读取失败,可通过 GetLastError 获取错误代码
}
CloseHandle(hFile);
return 0;
}
InternetWriteFile
InternetWriteFile
是 Windows API 中的一个函数,位于 WinINet 库中,用于向服务器发送 HTTP 请求的主体数据。
BOOL InternetWriteFile(
HINTERNET hFile,
LPCVOID lpBuffer,
DWORD dwNumberOfBytesToWrite,
LPDWORD lpdwNumberOfBytesWritten
);
参数说明:
hFile
: HTTP 请求句柄,通过调用HttpOpenRequest
函数返回。lpBuffer
: 指向包含要发送的数据的缓冲区指针。dwNumberOfBytesToWrite
: 要发送的数据的字节数。lpdwNumberOfBytesWritten
: 指向一个变量,用于接收实际写入的字节数。
InternetWriteFile
函数用于向服务器发送 HTTP 请求的主体数据,即请求体。在调用该函数时,需要先调用 HttpOpenRequest
函数创建一个 HTTP 请求句柄,并通过 InternetConnect
函数建立到服务器的连接。
使用方法示例:
HINTERNET hInternet = InternetOpen(...); // 打开一个 Internet 会话
HINTERNET hConnect = InternetConnect(hInternet, ...); // 建立到服务器的连接
HINTERNET hRequest = HttpOpenRequest(hConnect, ...); // 创建一个 HTTP 请求句柄
const char* requestData = "This is the request data.";
DWORD requestDataSize = strlen(requestData);
DWORD bytesWritten = 0;
// 发送 HTTP 请求的主体数据
BOOL bWrite = InternetWriteFile(hRequest, requestData, requestDataSize, &bytesWritten);
if (bWrite) {
// 请求体数据发送成功,可以继续处理服务器的响应数据
} else {
// 请求体数据发送失败,可以根据 GetLastError 获取错误代码进行相应的处理
}
// 关闭句柄,释放资源
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
HttpEndRequest
HttpEndRequest
是 Windows API 中的一个函数,位于 WinINet 库中,用于结束 HTTP 请求并接收服务器的响应。
BOOL HttpEndRequest(
HINTERNET hRequest,
LPINTERNET_BUFFERS lpBuffersOut,
DWORD dwFlags,
DWORD_PTR dwContext
);
参数说明:
-
hRequest
: HTTP 请求句柄,通过调用HttpOpenRequest
函数返回。 -
lpBuffersOut
: 指向一个INTERNET_BUFFERS
结构体的指针,用于接收服务器响应数据的缓冲区。 -
dwFlags
: 指定结束请求的标志,可以为以下常量的组合:
INTERNET_NO_CALLBACK
: 不使用回调函数。INTERNET_FLAG_KEEP_CONNECTION
: 在完成请求后保持与服务器的连接。INTERNET_FLAG_SECURE
: 使用 SSL 连接。- 其他标志,具体可参考官方文档。
-
dwContext
: 用户定义的上下文参数,将在异步操作时传递给回调函数。
HttpEndRequest
函数用于结束 HTTP 请求并接收服务器的响应。在调用该函数后,可以通过 lpBuffersOut
参数接收服务器返回的数据。在调用 HttpSendRequest
或 HttpSendRequestEx
函数后,应该使用 HttpEndRequest
函数来结束请求,以确保请求被正确发送给服务器并接收响应。
WriteFile
WriteFile
是 Windows API 中的一个函数,位于 kernel32.dll 中,用于向文件、管道、字符设备等对象中写入数据。
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
参数说明:
hFile
: 要写入数据的文件、管道或设备的句柄。lpBuffer
: 指向包含要写入数据的缓冲区。nNumberOfBytesToWrite
: 要写入的字节数。lpNumberOfBytesWritten
: 指向一个变量,用于接收实际写入的字节数。lpOverlapped
: 用于异步操作的OVERLAPPED
结构体指针,可设置为 NULL 表示使用同步操作。
WriteFile
函数用于向指定的文件、管道或设备中写入数据,并将指定的数据写入到缓冲区中。如果写入成功,则返回 TRUE,并通过 lpNumberOfBytesWritten
参数返回实际写入的字节数。如果写入失败,则返回 FALSE,并可以通过调用 GetLastError
函数获取错误代码。
使用方法示例:
#include <Windows.h>
int main() {
HANDLE hFile = CreateFile(L"C:\\path\\to\\file.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
// 处理文件打开失败的情况
return -1;
}
const char* dataToWrite = "Hello, WriteFile!";
DWORD dataSize = strlen(dataToWrite);
DWORD bytesWritten = 0;
if (WriteFile(hFile, dataToWrite, dataSize, &bytesWritten, NULL)) {
// 写入成功,bytesWritten 中存储了实际写入的字节数
} else {
// 写入失败,可通过 GetLastError 获取错误代码
}
CloseHandle(hFile);
return 0;
}
SetEvent
SetEvent
是 Windows API 中的一个函数,位于 kernel32.dll 中,用于将指定的事件对象的状态设置为 signaled(即触发状态)。
BOOL SetEvent(
HANDLE hEvent
);
参数说明:
hEvent
: 要设置为 signaled 状态的事件对象的句柄。
SetEvent
函数用于将指定的事件对象的状态设置为 signaled。如果事件对象的初始状态为非 signaled(未触发状态),则调用 SetEvent
函数后,事件对象的状态会变为 signaled(触发状态),从而可以通知等待该事件对象的线程,使其继续执行。
使用方法示例:
#include <Windows.h>
int main() {
// 创建一个自动重置的事件对象
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent == NULL) {
// 处理事件对象创建失败的情况
return -1;
}
// 等待事件对象的状态变为 signaled
WaitForSingleObject(hEvent, INFINITE);
// 继续执行其他操作
CloseHandle(hEvent);
return 0;
}
WinHttp简介:
什么是WinHttp?
Microsoft Windows HTTP Services (WinHTTP) 提供服务器支持的 HTTP/2 和 1.1 Internet 协议的高级接口。 WinHTTP 主要用于与 HTTP 服务器通信的服务器应用程序在基于服务器的方案中。
API的使用步骤如下:
封装类
编译器使用的是vs2019,然后json库和编码转换以及类之间的通信使用的是Qt的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XQgpmpKA-1693216132597)(…\Image\企业微信截图_20230814161504.png)]
WinHttpRequest类是提供给调用者使用的,WinHttpThread则是实际发送http请求的地方;
大致流程如下:
调用者将所需参数传入winHttpRequest中,WinHttpRequest将参数转换后传入WinHttpThread中,如果指定的是异步则会创建一个线程,并将此线程传入线程池中,如果不是则会同步堵塞当前线程。在WinHttpThread中http处理完成后则会将信息传回到WinHttpRequest中,由WinHttpRequest通知调用者http返回的结果;
WinHttpRequest.h
#pragma once
#include <QObject>
#include <QThreadPool>
#include "WinHttpThread.h"
class WinHttpRequest :
public QObject
{
Q_OBJECT
public:
enum CONNECTIO_ACTION
{
SYNCHRONOUS,
ASYNCHRONOUS,
};
Q_ENUM(CONNECTIO_ACTION)
WinHttpRequest(QObject *parent=nullptr);
~WinHttpRequest();
static WinHttpRequest* getInstance();
/// <summary>
/// 开始Http请求
/// </summary>
/// <param name="url">资源路径</param>
/// <param name="headetContentType">告知服务端文本格式</param>
/// <param name="model">请求谓词</param>
/// <param name="requestType">用来表示每一个http请求的枚举值</param>
/// <param name="params">body中传入的参数</param>
/// <param name="connection">同步或异步</param>
void httpRequest(const QString url, const QString headetContentType = "", const WinHttpThread::HttpRequestMode model = WinHttpThread::GET, const WinHttpThread::HttpRequestType requestType = WinHttpThread::HTTP_REQUEST_LOGIN, const QVariantMap params = {}, CONNECTIO_ACTION connection = ASYNCHRONOUS);
public: signals:
void sigHttpRequestReply(int code, WinHttpThread::HttpRequestType requestType, QByteArray data);
private:
QThreadPool m_threadPool;
};
WinHttpRequest.cpp
#include "WinHttpRequest.h"
#include <iostream>
#include <strsafe.h>
WinHttpRequest::WinHttpRequest(QObject* parent)
:QObject(parent)
{
m_threadPool.setMaxThreadCount(3);
}
WinHttpRequest::~WinHttpRequest()
{
m_threadPool.clear();
m_threadPool.waitForDone(3000);
}
WinHttpRequest* WinHttpRequest::getInstance()
{
static WinHttpRequest winHttpRequest;
return &winHttpRequest;
}
void WinHttpRequest::httpRequest(const QString url, const QString headetContentType, const WinHttpThread::HttpRequestMode model, const WinHttpThread::HttpRequestType requestType, const QVariantMap params, CONNECTIO_ACTION connection) {
if (connection == ASYNCHRONOUS) {
WinHttpThread* thread = new WinHttpThread(url, headetContentType, model, requestType, params);
connect(thread, &WinHttpThread::sigHttpRequested, this, [=](int code, WinHttpThread::HttpRequestType type, QByteArray recev) {
qDebug() << "code:" << code << " type" << type << " recev" << QString::fromUtf8(recev);
});
m_threadPool.start(thread);
}
else {
WinHttpThread thread(url, headetContentType, model, requestType, params);
WinHttpThread::MsgData msgData;
if (thread.doWork(msgData)) {
qDebug() << "code:" << msgData.statuscode << " type:" << msgData.reqType << " recev" << QString::fromUtf8(msgData.data);
}
}
}
WinHttpThread.h
#pragma once
#include <qobject.h>
#include <QThread>
#include <QRunnable>
#include <QObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <windows.h>
#include <winhttp.h>
#include <QString>
#include <QThreadPool>
#include <mutex>
#pragma comment (lib,"Winhttp")
typedef struct URLData {
QString hostName;
QString resourceUrl;
int port;
URLData() {
hostName = "";
resourceUrl = "";
port = INTERNET_DEFAULT_HTTP_PORT;
}
}URLData_t;
class WinHttpThread :
public QObject,public QRunnable
{
Q_OBJECT
public:
enum HttpRequestMode {
GET,
POST,
HEAD,
PUT,
DELETE_N,
CONNECT,
OPTIONS,
TRACE,
DEFAULT
};
Q_ENUM(HttpRequestMode)
enum HttpRequestType {
HTTP_REQUEST_LOGIN,
HTTP_REQUEST_USERCONFIG,
HTTP_REQUEST_GETPAGE
};
Q_ENUM(HttpRequestType)
typedef struct MsgData {
DWORD statuscode;
HttpRequestType reqType;
QByteArray data;
}MsgData_t;
WinHttpThread() = delete;
WinHttpThread(const QString url, const QString headetContentType = "", const HttpRequestMode model = GET, const HttpRequestType requestType = HTTP_REQUEST_LOGIN, const QVariantMap params = {});
~WinHttpThread();
//时机处理http请求的地方
bool doWork(MsgData& data);
public:signals:
void sigHttpRequested(int code, HttpRequestType type, QByteArray recev);
protected:
void init();
void run();
bool analyseUrl(const QString& url);
template <typename T>
void setRawHeader(QString key, T value);
private:
void addHeaderToHttp(HINTERNET &hRequest);
LPCWSTR getReqModel(HttpRequestMode reqModel);
void ReleaseHandle();
private:
HINTERNET m_heSession;
HINTERNET m_hConnect;
HINTERNET m_hRequest;
bool isInit = false;
QMap<QString, QString> m_map;//header
QVariantMap m_params;//body
QString m_url = "";
QString m_headerContentType = "";
HttpRequestMode m_model;
HttpRequestType m_requestType;
URLData_t m_urlData;
const LPCWSTR m_appName = L"vlive";
std::mutex m_mutex;
QByteArray data;
};
WinHttpThread.cpp
#include "WinHttpThread.h"
WinHttpThread::WinHttpThread(const QString url, const QString headetContentType, const HttpRequestMode model, const HttpRequestType requestType, const QVariantMap params)
:m_url(url),m_headerContentType(headetContentType),m_model(model),m_requestType(requestType), m_params(params)
{
isInit = analyseUrl(m_url);
//此处设置http头部内容
m_map.insert("browser_kernel", "1.6.2");
m_map.insert("device", "DDF935926350BAF0372FB0058DB22687CC75F03580B02FCDBC5D6A0B");
m_map.insert("version", "1.6.2.0");
m_map.insert("Content-type", headetContentType);
}
WinHttpThread::~WinHttpThread()
{
ReleaseHandle();
}
void WinHttpThread::init()
{
}
void WinHttpThread::run()
{
MsgData data;
if (doWork(data)) {
emit sigHttpRequested(data.statuscode, data.reqType, data.data);
}
ReleaseHandle();
}
bool WinHttpThread::analyseUrl(const QString& url)
{
QUrl qurl(url);
DWORD error;
if (!qurl.isValid()) {
qDebug() << "no vailed url." << url;
return false;
}
m_urlData.hostName = qurl.host();
m_urlData.resourceUrl = qurl.path();
QString str = qurl.scheme();
if (str == "https") {
m_urlData.port = INTERNET_DEFAULT_HTTPS_PORT;
}
else if (str == "http") {
m_urlData.port = INTERNET_DEFAULT_HTTP_PORT;
}
else {
qDebug() << "unkonw url." << url;
return false;
}
return true;
}
void WinHttpThread::addHeaderToHttp(HINTERNET& hRequest)
{
if (!hRequest) {
qDebug() << "hRequest is null...";
return;
}
QMap<QString,QString>::const_iterator iteral;
BOOL re = FALSE;
QString data = "";
std::wstring dataC = L"";
DWORD error;
for (iteral = m_map.cbegin(); iteral != m_map.cend(); iteral++) {
data = iteral.key() + ": " + iteral.value();
dataC = data.toStdWString();
re = WinHttpAddRequestHeaders(hRequest, dataC.c_str(), dataC.size(), WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE);
if (re) {
error = GetLastError();
qDebug() << "header:" << data << "error:" << error;
}
}
}
LPCWSTR WinHttpThread::getReqModel(HttpRequestMode reqModel)
{
switch (reqModel) {
case GET:
return L"GET";
break;
case POST:
return L"POST";
break;
case PUT:
return L"PUT";
break;
case DELETE_N:
return L"DELETE";
break;
default:
return L"";
break;
}
}
void WinHttpThread::ReleaseHandle()
{
if (m_mutex.try_lock()) {
if (m_hRequest) WinHttpCloseHandle(m_hRequest);
if (m_hConnect) WinHttpCloseHandle(m_hConnect);
if (m_heSession) WinHttpCloseHandle(m_heSession);
m_mutex.unlock();
}
}
bool WinHttpThread::doWork(MsgData& msgData) {
if (!isInit)return false;
DWORD error;
m_heSession = WinHttpOpen(m_appName, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!m_heSession) {
error = GetLastError();
qDebug() << "WinHttpOpen:" << "error:" << error;
}
LPCWSTR model = getReqModel(m_model);
m_hConnect = WinHttpConnect(m_heSession, m_urlData.hostName.toStdWString().c_str(), m_urlData.port, 0);
if (!m_hConnect) {
error = GetLastError();
qDebug() << "WinHttpConnect error:" << error;
ReleaseHandle();
return false;
}
m_hRequest = WinHttpOpenRequest(m_hConnect, model, m_urlData.resourceUrl.toStdWString().c_str(), NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!m_hRequest) {
error = GetLastError();
qDebug() << "WinHttpConnect error:" << error;
ReleaseHandle();
return false;
}
//body
QByteArray byte = "";
if (!m_params.isEmpty()) {
byte = QJsonDocument::fromVariant(m_params).toJson().data();
}
//init httpheader
addHeaderToHttp(m_hRequest);
std::string strB = byte.toStdString();
//send && init body
if (!WinHttpSendRequest(m_hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)strB.c_str(), strB.size(), strB.size(), 0)) {
error = GetLastError();
qDebug() << "WinHttpSendRequest error:" << error;
ReleaseHandle();
return false;
}
if (!WinHttpReceiveResponse(m_hRequest, 0)) {
error = GetLastError();
qDebug() << "WinHttpReceiveResponse error:" << error;
ReleaseHandle();
return false;
}
//get status code
DWORD statusCode;
DWORD dwSize = sizeof(statusCode);
DWORD recevicedSize;
WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&statusCode, &dwSize, WINHTTP_NO_HEADER_INDEX);
//READ data
LPSTR pszOutBuffer;
QByteArray data;
do {
dwSize = 0;
if (!WinHttpQueryDataAvailable(m_hRequest, &dwSize)) {
error = GetLastError();
qDebug() << "WinHttpQueryDataAvailable error:" << error;
ReleaseHandle();
return false;
}
else {
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer) {
qDebug() << "out memory...";
dwSize = 0;
}
else {
ZeroMemory(pszOutBuffer, dwSize + 1);
//读取处理的数据编码为uincode可能会乱码,需要转为utf-8。此处不转换,有外部转换
if (!WinHttpReadData(m_hRequest, (LPVOID)pszOutBuffer,
dwSize, &recevicedSize)) {
error = GetLastError();
qDebug() << "WinHttpQueryDataAvailable error:" << error;
ReleaseHandle();
delete[] pszOutBuffer;
return false;
}
else {
data.append(pszOutBuffer);
}
delete[] pszOutBuffer;
}
}
} while (dwSize > 0);
msgData.statuscode = statusCode;
msgData.data = data;
msgData.reqType = m_requestType;
return true;
}
例子
QVariantMap params;
params.insert("name", "hqluo");
WinHttpRequest::getInstance()->httpRequest("https://www.baidu.com"
, "application/json", WinHttpThread::HttpRequestMode::GET,WinHttpThread::HttpRequestType::HTTP_REQUEST_GETPAGE, params,WinHttpRequest::ASYNCHRONOUS);
//此处我们将http的请求体中的内容设置为{"name":"hqluo"},并且告知服务端数据格式为json格式,以及模式设置为异步模式