WinInet(Windows Internet)是 Microsoft Windows 操作系統(tǒng)中的一個(gè) API 集,用于提供對(duì) Internet 相關(guān)功能的支持。它包括了一系列的函數(shù),使得 Windows 應(yīng)用程序能夠進(jìn)行網(wǎng)絡(luò)通信、處理 HTTP 請(qǐng)求、FTP 操作等。WinInet 提供了一套完整的網(wǎng)絡(luò)通信工具,使得開發(fā)者能夠輕松地構(gòu)建支持網(wǎng)絡(luò)功能的應(yīng)用程序,涵蓋了從簡(jiǎn)單的 HTTP 請(qǐng)求到復(fù)雜的文件傳輸?shù)榷喾N網(wǎng)絡(luò)操作。
分解URL地址
InternetCrackUrl
函數(shù)可實(shí)現(xiàn)對(duì)URL字符串進(jìn)行解析,提取其中的協(xié)議、主機(jī)名、端口、路徑和其他信息,并將這些信息存儲(chǔ)在 URL_COMPONENTS
結(jié)構(gòu)中,方便后續(xù)的網(wǎng)絡(luò)操作,該函數(shù)是Windows下默認(rèn)提供的,函數(shù)與依賴結(jié)果如下所示;
函數(shù)原型
BOOL InternetCrackUrl(
LPCTSTR lpszUrl,
DWORD dwUrlLength,
DWORD dwFlags,
LPURL_COMPONENTS lpUrlComponents
);
參數(shù)說(shuō)明
-
lpszUrl
:指定待解析的 URL 字符串。 -
dwUrlLength
:指定 URL 字符串的長(zhǎng)度。 -
dwFlags
:指定解析 URL 的標(biāo)志,可以是以下值之一:-
ICU_DECODE
:對(duì) URL 進(jìn)行解碼。 -
ICU_ESCAPE
:對(duì) URL 進(jìn)行轉(zhuǎn)義。
-
-
lpUrlComponents
:一個(gè)指向URL_COMPONENTS
結(jié)構(gòu)的指針,用于存儲(chǔ)解析后的各個(gè)部分信息。
URL_COMPONENTS結(jié)構(gòu)
typedef struct {
DWORD dwStructSize;
LPTSTR lpszScheme;
DWORD dwSchemeLength;
INTERNET_SCHEME nScheme;
LPTSTR lpszHostName;
DWORD dwHostNameLength;
INTERNET_PORT nPort;
LPTSTR lpszUserName;
DWORD dwUserNameLength;
LPTSTR lpszPassword;
DWORD dwPasswordLength;
LPTSTR lpszUrlPath;
DWORD dwUrlPathLength;
LPTSTR lpszExtraInfo;
DWORD dwExtraInfoLength;
} URL_COMPONENTS, *LPURL_COMPONENTS;
返回值
如果函數(shù)成功,返回 TRUE
,并在 lpUrlComponents
結(jié)構(gòu)中存儲(chǔ)解析后的信息;如果失敗,返回 FALSE
。在失敗時(shí),可以調(diào)用 GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
函數(shù)調(diào)用
#include <iostream>
#include <Windows.h>
#include <WinInet.h>
#pragma comment(lib, "WinInet.lib")
using namespace std;
BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, DWORD dwBufferSize)
{
BOOL bRet = FALSE;
URL_COMPONENTS uc = { 0 };
// 初始化變量中的內(nèi)容
RtlZeroMemory(&uc, sizeof(uc));
RtlZeroMemory(pszScheme, dwBufferSize);
RtlZeroMemory(pszHostName, dwBufferSize);
RtlZeroMemory(pszUserName, dwBufferSize);
RtlZeroMemory(pszPassword, dwBufferSize);
RtlZeroMemory(pszUrlPath, dwBufferSize);
RtlZeroMemory(pszExtraInfo, dwBufferSize);
// 將長(zhǎng)度填充到結(jié)構(gòu)中
uc.dwStructSize = sizeof(uc);
uc.dwSchemeLength = dwBufferSize - 1;
uc.dwHostNameLength = dwBufferSize - 1;
uc.dwUserNameLength = dwBufferSize - 1;
uc.dwPasswordLength = dwBufferSize - 1;
uc.dwUrlPathLength = dwBufferSize - 1;
uc.dwExtraInfoLength = dwBufferSize - 1;
uc.lpszScheme = pszScheme;
uc.lpszHostName = pszHostName;
uc.lpszUserName = pszUserName;
uc.lpszPassword = pszPassword;
uc.lpszUrlPath = pszUrlPath;
uc.lpszExtraInfo = pszExtraInfo;
// 分解URL地址
bRet = InternetCrackUrl(pszUrl, 0, 0, &uc);
if (FALSE == bRet)
{
return bRet;
}
return bRet;
}
int main(int argc, char* argv[])
{
char szHttpDownloadUrl[] = "http://www.lyshark.com/index.php&username=lyshark&password=123";
// 對(duì)應(yīng)的變量
char szScheme[MAX_PATH] = { 0 };
char szHostName[MAX_PATH] = { 0 };
char szUserName[MAX_PATH] = { 0 };
char szPassword[MAX_PATH] = { 0 };
char szUrlPath[MAX_PATH] = { 0 };
char szExtraInfo[MAX_PATH] = { 0 };
// 初始化用0填充
RtlZeroMemory(szScheme, MAX_PATH);
RtlZeroMemory(szHostName, MAX_PATH);
RtlZeroMemory(szUserName, MAX_PATH);
RtlZeroMemory(szPassword, MAX_PATH);
RtlZeroMemory(szUrlPath, MAX_PATH);
RtlZeroMemory(szExtraInfo, MAX_PATH);
// 分解URL
if (FALSE == UrlCrack(szHttpDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))
{
return FALSE;
}
std::cout << szScheme << std::endl;
std::cout << szHostName << std::endl;
std::cout << szUserName << std::endl;
std::cout << szPassword << std::endl;
std::cout << szUrlPath << std::endl;
std::cout << szExtraInfo << std::endl;
system("pause");
return 0;
}
運(yùn)行代碼輸出特定網(wǎng)址的每個(gè)部分,如下圖所示;
下載頁(yè)面內(nèi)容
InternetOpen
用于初始化 WinINet 函數(shù)的使用。以下是該函數(shù)的原型:
HINTERNET InternetOpen(
LPCWSTR lpszAgent,
DWORD dwAccessType,
LPCWSTR lpszProxyName,
LPCWSTR lpszProxyBypass,
DWORD dwFlags
);
參數(shù)說(shuō)明:
-
lpszAgent
: 指定應(yīng)用程序的名稱,用于標(biāo)識(shí)調(diào)用InternetOpen
的應(yīng)用程序。 -
dwAccessType
: 指定訪問(wèn)類型,可以是INTERNET_OPEN_TYPE_DIRECT
、INTERNET_OPEN_TYPE_PROXY
或INTERNET_OPEN_TYPE_PRECONFIG
中的一個(gè)。 -
lpszProxyName
: 如果dwAccessType
是INTERNET_OPEN_TYPE_PROXY
,則指定代理服務(wù)器的名稱。否則,可以設(shè)為NULL
。 -
lpszProxyBypass
: 如果dwAccessType
是INTERNET_OPEN_TYPE_PROXY
,則指定繞過(guò)代理服務(wù)器的地址。否則,可以設(shè)為NULL
。 -
dwFlags
: 一些標(biāo)志,可以用來(lái)指定額外的行為,如INTERNET_FLAG_ASYNC
用于異步操作。
返回值:
如果函數(shù)調(diào)用成功,將返回一個(gè)類型為 HINTERNET
的句柄,用于后續(xù)的 WinINet 操作。如果函數(shù)調(diào)用失敗,返回 NULL
??梢允褂?GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
InternetConnect
用于建立到遠(yuǎn)程服務(wù)器的連接。以下是該函數(shù)的原型:
HINTERNET InternetConnect(
HINTERNET hInternet,
LPCWSTR lpszServerName,
INTERNET_PORT nServerPort,
LPCWSTR lpszUserName,
LPCWSTR lpszPassword,
DWORD dwService,
DWORD dwFlags,
DWORD_PTR dwContext
);
參數(shù)說(shuō)明:
-
hInternet
: 調(diào)用InternetOpen
返回的句柄,表示連接的上下文。 -
lpszServerName
: 要連接的服務(wù)器的名稱或 IP 地址。 -
nServerPort
: 服務(wù)器的端口號(hào)。 -
lpszUserName
: 連接服務(wù)器時(shí)要使用的用戶名,可以為NULL
。 -
lpszPassword
: 連接服務(wù)器時(shí)要使用的密碼,可以為NULL
。 -
dwService
: 指定服務(wù)類型,可以是INTERNET_SERVICE_FTP
、INTERNET_SERVICE_HTTP
或其他服務(wù)類型。 -
dwFlags
: 一些標(biāo)志,用于指定連接的屬性,如INTERNET_FLAG_RELOAD
、INTERNET_FLAG_SECURE
等。 -
dwContext
: 用戶定義的應(yīng)用程序上下文,將在回調(diào)函數(shù)中使用。
返回值:
如果函數(shù)調(diào)用成功,將返回一個(gè)類型為 HINTERNET
的句柄,表示連接的上下文。如果函數(shù)調(diào)用失敗,返回 NULL
??梢允褂?GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
InternetConnect
用于建立連接后,可以使用返回的句柄執(zhí)行相關(guān)的協(xié)議操作,如 FTP 或 HTTP 操作。使用完連接后,同樣需要使用 InternetCloseHandle
函數(shù)關(guān)閉相應(yīng)的句柄,以釋放資源。
HttpOpenRequest
它是在使用 WinINet 庫(kù)進(jìn)行 HTTP 操作時(shí)的一部分。以下是該函數(shù)的原型:
HINTERNET HttpOpenRequest(
HINTERNET hConnect,
LPCWSTR lpszVerb,
LPCWSTR lpszObjectName,
LPCWSTR lpszVersion,
LPCWSTR lpszReferrer,
LPCWSTR *lplpszAcceptTypes,
DWORD dwFlags,
DWORD_PTR dwContext
);
參數(shù)說(shuō)明:
-
hConnect
: 調(diào)用InternetConnect
返回的連接句柄,表示請(qǐng)求的上下文。 -
lpszVerb
: HTTP 請(qǐng)求方法,如 “GET”、“POST” 等。 -
lpszObjectName
: 請(qǐng)求的對(duì)象名,通常是 URL 的路徑部分。 -
lpszVersion
: HTTP 協(xié)議版本,通常是 “HTTP/1.1”。 -
lpszReferrer
: 引用的來(lái)源,可以為NULL
。 -
lplpszAcceptTypes
: 指定可接受的媒體類型,可以為NULL
。 -
dwFlags
: 一些標(biāo)志,用于指定請(qǐng)求的屬性,如INTERNET_FLAG_RELOAD
、INTERNET_FLAG_SECURE
等。 -
dwContext
: 用戶定義的應(yīng)用程序上下文,將在回調(diào)函數(shù)中使用。
返回值:
如果函數(shù)調(diào)用成功,將返回一個(gè)類型為 HINTERNET
的句柄,表示打開的 HTTP 請(qǐng)求。如果函數(shù)調(diào)用失敗,返回 NULL
。可以使用 GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
一旦打開了 HTTP 請(qǐng)求,可以使用返回的句柄執(zhí)行發(fā)送請(qǐng)求、接收響應(yīng)等操作。使用完請(qǐng)求后,同樣需要使用 InternetCloseHandle
函數(shù)關(guān)閉相應(yīng)的句柄,以釋放資源。
HttpSendRequest
用于發(fā)送 HTTP 請(qǐng)求的函數(shù),通常在使用 WinINet 庫(kù)進(jìn)行 HTTP 操作時(shí)調(diào)用。以下是該函數(shù)的原型:
BOOL HttpSendRequest(
HINTERNET hRequest,
LPCWSTR lpszHeaders,
DWORD dwHeadersLength,
LPVOID lpOptional,
DWORD dwOptionalLength
);
參數(shù)說(shuō)明:
-
hRequest
: 調(diào)用HttpOpenRequest
返回的 HTTP 請(qǐng)求句柄,表示要發(fā)送請(qǐng)求的上下文。 -
lpszHeaders
: 包含請(qǐng)求頭信息的字符串,可以為NULL
。 -
dwHeadersLength
: 請(qǐng)求頭的長(zhǎng)度,如果lpszHeaders
是NULL
,則可以為零。 -
lpOptional
: 包含請(qǐng)求的可選數(shù)據(jù)的緩沖區(qū),可以為NULL
。 -
dwOptionalLength
: 可選數(shù)據(jù)的長(zhǎng)度,如果lpOptional
是NULL
,則可以為零。
返回值:
如果函數(shù)調(diào)用成功,返回非零值;如果函數(shù)調(diào)用失敗,返回零??梢允褂?GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
HttpSendRequest
用于實(shí)際發(fā)送 HTTP 請(qǐng)求。在調(diào)用此函數(shù)之后,可以使用其他 WinINet 函數(shù)來(lái)讀取服務(wù)器的響應(yīng)。同樣,使用完請(qǐng)求后,需要使用 InternetCloseHandle
函數(shù)關(guān)閉相應(yīng)的句柄,以釋放資源。
HttpQueryInfo
用于檢索有關(guān) HTTP 請(qǐng)求或響應(yīng)的信息的函數(shù),通常在使用 WinINet 庫(kù)進(jìn)行 HTTP 操作時(shí)調(diào)用。以下是該函數(shù)的原型:
BOOL HttpQueryInfo(
HINTERNET hRequest,
DWORD dwInfoLevel,
LPVOID lpBuffer,
LPDWORD lpdwBufferLength,
LPDWORD lpdwIndex
);
參數(shù)說(shuō)明:
-
hRequest
: 調(diào)用HttpOpenRequest
返回的 HTTP 請(qǐng)求句柄,表示要查詢信息的上下文。 -
dwInfoLevel
: 指定要檢索的信息類型,可以是預(yù)定義的常量,如HTTP_QUERY_STATUS_CODE
、HTTP_QUERY_CONTENT_TYPE
等。 -
lpBuffer
: 用于接收檢索到的信息的緩沖區(qū)。 -
lpdwBufferLength
: 指向一個(gè)變量,表示lpBuffer
緩沖區(qū)的大小。在調(diào)用函數(shù)前,應(yīng)該將該變量設(shè)置為lpBuffer
緩沖區(qū)的大小。在調(diào)用函數(shù)后,該變量將包含實(shí)際寫入緩沖區(qū)的字節(jié)數(shù)。 -
lpdwIndex
: 如果請(qǐng)求返回多個(gè)值,可以使用此參數(shù)指定要檢索的值的索引。對(duì)于單值的信息,可以將其設(shè)置為NULL
。
返回值:
如果函數(shù)調(diào)用成功,返回非零值;如果函數(shù)調(diào)用失敗,返回零。可以使用 GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
HttpQueryInfo
用于獲取與 HTTP 請(qǐng)求或響應(yīng)相關(guān)的信息,如狀態(tài)碼、內(nèi)容類型等。注意,在調(diào)用此函數(shù)之前,通常需要先調(diào)用 HttpSendRequest
發(fā)送請(qǐng)求。同樣,使用完請(qǐng)求后,需要使用 InternetCloseHandle
函數(shù)關(guān)閉相應(yīng)的句柄,以釋放資源。
InternetReadFile
用于從指定的句柄讀取數(shù)據(jù)的函數(shù),通常在使用 WinINet 庫(kù)進(jìn)行網(wǎng)絡(luò)操作時(shí)調(diào)用。以下是該函數(shù)的原型:
BOOL InternetReadFile(
HINTERNET hFile,
LPVOID lpBuffer,
DWORD dwNumberOfBytesToRead,
LPDWORD lpdwNumberOfBytesRead
);
參數(shù)說(shuō)明:
-
hFile
: 調(diào)用HttpOpenRequest
或FtpOpenFile
返回的句柄,表示要讀取數(shù)據(jù)的上下文。 -
lpBuffer
: 用于接收讀取到的數(shù)據(jù)的緩沖區(qū)。 -
dwNumberOfBytesToRead
: 指定要讀取的字節(jié)數(shù)。 -
lpdwNumberOfBytesRead
: 指向一個(gè)變量,表示lpBuffer
緩沖區(qū)中實(shí)際讀取的字節(jié)數(shù)。在調(diào)用函數(shù)前,應(yīng)該將該變量設(shè)置為lpBuffer
緩沖區(qū)的大小。在調(diào)用函數(shù)后,該變量將包含實(shí)際讀取的字節(jié)數(shù)。
返回值:
如果函數(shù)調(diào)用成功,返回非零值;如果函數(shù)調(diào)用失敗,返回零??梢允褂?GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
InternetReadFile
用于從網(wǎng)絡(luò)資源中讀取數(shù)據(jù),如從 HTTP 請(qǐng)求的響應(yīng)中讀取內(nèi)容。在調(diào)用此函數(shù)之前,通常需要先調(diào)用其他相關(guān)的函數(shù),如 HttpOpenRequest
、HttpSendRequest
和 HttpQueryInfo
。同樣,使用完資源后,需要使用 InternetCloseHandle
函數(shù)關(guān)閉相應(yīng)的句柄,以釋放資源。
下載頁(yè)面的完整代碼是這樣的,如下所示;
#include <iostream>
#include <Windows.h>
#include <WinInet.h>
#pragma comment(lib, "WinInet.lib")
using namespace std;
BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, DWORD dwBufferSize)
{
BOOL bRet = FALSE;
URL_COMPONENTS uc = { 0 };
// 初始化變量中的內(nèi)容
RtlZeroMemory(&uc, sizeof(uc));
RtlZeroMemory(pszScheme, dwBufferSize);
RtlZeroMemory(pszHostName, dwBufferSize);
RtlZeroMemory(pszUserName, dwBufferSize);
RtlZeroMemory(pszPassword, dwBufferSize);
RtlZeroMemory(pszUrlPath, dwBufferSize);
RtlZeroMemory(pszExtraInfo, dwBufferSize);
// 將長(zhǎng)度填充到結(jié)構(gòu)中
uc.dwStructSize = sizeof(uc);
uc.dwSchemeLength = dwBufferSize - 1;
uc.dwHostNameLength = dwBufferSize - 1;
uc.dwUserNameLength = dwBufferSize - 1;
uc.dwPasswordLength = dwBufferSize - 1;
uc.dwUrlPathLength = dwBufferSize - 1;
uc.dwExtraInfoLength = dwBufferSize - 1;
uc.lpszScheme = pszScheme;
uc.lpszHostName = pszHostName;
uc.lpszUserName = pszUserName;
uc.lpszPassword = pszPassword;
uc.lpszUrlPath = pszUrlPath;
uc.lpszExtraInfo = pszExtraInfo;
// 分解URL地址
bRet = InternetCrackUrl(pszUrl, 0, 0, &uc);
if (FALSE == bRet)
{
return bRet;
}
return bRet;
}
// 從響應(yīng)信息頭信息中獲取數(shù)據(jù)內(nèi)容長(zhǎng)度大小
BOOL GetContentLength(char* pResponseHeader, DWORD* pdwContentLength)
{
int i = 0;
char szContentLength[MAX_PATH] = { 0 };
DWORD dwContentLength = 0;
char szSubStr[] = "Content-Length: ";
RtlZeroMemory(szContentLength, MAX_PATH);
// 在傳入字符串中查找子串
char* p = strstr(pResponseHeader, szSubStr);
if (NULL == p)
{
return FALSE;
}
p = p + lstrlen(szSubStr);
// 如果找到了就提取出里面的純數(shù)字
while (('0' <= *p) && ('9' >= *p))
{
szContentLength[i] = *p;
p++;
i++;
}
// 字符串轉(zhuǎn)數(shù)字
dwContentLength = atoi(szContentLength);
*pdwContentLength = dwContentLength;
return TRUE;
}
// 數(shù)據(jù)下載
BOOL HttpDownload(char* pszDownloadUrl, BYTE** ppDownloadData, DWORD* pdwDownloadDataSize)
{
// 定義HTTP子變量
char szScheme[MAX_PATH] = { 0 };
char szHostName[MAX_PATH] = { 0 };
char szUserName[MAX_PATH] = { 0 };
char szPassword[MAX_PATH] = { 0 };
char szUrlPath[MAX_PATH] = { 0 };
char szExtraInfo[MAX_PATH] = { 0 };
// 填充為空
RtlZeroMemory(szScheme, MAX_PATH);
RtlZeroMemory(szHostName, MAX_PATH);
RtlZeroMemory(szUserName, MAX_PATH);
RtlZeroMemory(szPassword, MAX_PATH);
RtlZeroMemory(szUrlPath, MAX_PATH);
RtlZeroMemory(szExtraInfo, MAX_PATH);
// 拆解URL地址
if (FALSE == UrlCrack(pszDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))
{
return FALSE;
}
// 數(shù)據(jù)下載
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
DWORD dwOpenRequestFlags = 0;
BOOL bRet = FALSE;
unsigned char* pResponseHeaderIInfo = NULL;
DWORD dwResponseHeaderIInfoSize = 2048;
BYTE* pBuf = NULL;
DWORD dwBufSize = 64 * 1024;
BYTE* pDownloadData = NULL;
DWORD dwDownloadDataSize = 0;
DWORD dwRet = 0;
DWORD dwOffset = 0;
do
{
// 建立會(huì)話
hInternet = InternetOpen("WinInetGet/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (NULL == hInternet)
{
break;
}
// 建立連接
hConnect = InternetConnect(hInternet, szHostName, INTERNET_DEFAULT_HTTP_PORT, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);
if (NULL == hConnect)
{
break;
}
// 打開并發(fā)送HTTP請(qǐng)求
dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_AUTH |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD;
if (0 < lstrlen(szExtraInfo))
{
lstrcat(szUrlPath, szExtraInfo);
}
// 以GET模式打開請(qǐng)求
hRequest = HttpOpenRequest(hConnect, "GET", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
if (NULL == hRequest)
{
break;
}
// 發(fā)送請(qǐng)求
bRet = HttpSendRequest(hRequest, NULL, 0, NULL, 0);
if (FALSE == bRet)
{
break;
}
// 接收響應(yīng)的報(bào)文信息頭(Get Response Header)
pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize];
if (NULL == pResponseHeaderIInfo)
{
break;
}
RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);
// 查詢HTTP請(qǐng)求頭
bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);
if (FALSE == bRet)
{
break;
}
// 從字符串中 "Content-Length: " 網(wǎng)頁(yè)獲取數(shù)據(jù)長(zhǎng)度
bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwDownloadDataSize);
// 輸出完整響應(yīng)頭
std::cout << pResponseHeaderIInfo << std::endl;
if (FALSE == bRet)
{
break;
}
// 接收?qǐng)?bào)文主體內(nèi)容(Get Response Body)
pBuf = new BYTE[dwBufSize];
if (NULL == pBuf)
{
break;
}
pDownloadData = new BYTE[dwDownloadDataSize];
if (NULL == pDownloadData)
{
break;
}
RtlZeroMemory(pDownloadData, dwDownloadDataSize);
do
{
RtlZeroMemory(pBuf, dwBufSize);
// 循環(huán)讀入數(shù)據(jù)并保存在變量中
bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);
if (FALSE == bRet)
{
break;
}
RtlCopyMemory((pDownloadData + dwOffset), pBuf, dwRet);
dwOffset = dwOffset + dwRet;
} while (dwDownloadDataSize > dwOffset);
// 返回?cái)?shù)據(jù)
*ppDownloadData = pDownloadData;
*pdwDownloadDataSize = dwDownloadDataSize;
} while (FALSE);
// 關(guān)閉并釋放資源
if (NULL != pBuf)
{
delete[]pBuf;
pBuf = NULL;
}
if (NULL != pResponseHeaderIInfo)
{
delete[]pResponseHeaderIInfo;
pResponseHeaderIInfo = NULL;
}
if (NULL != hRequest)
{
InternetCloseHandle(hRequest);
hRequest = NULL;
}
if (NULL != hConnect)
{
InternetCloseHandle(hConnect);
hConnect = NULL;
}
if (NULL != hInternet)
{
InternetCloseHandle(hInternet);
hInternet = NULL;
}
return bRet;
}
// 創(chuàng)建并保存文件
BOOL SaveToFile(char* pszFileName, BYTE* pData, DWORD dwDataSize)
{
// 創(chuàng)建空文件
HANDLE hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_ARCHIVE, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
return FALSE;
}
DWORD dwRet = 0;
// 寫出數(shù)據(jù)到文件
WriteFile(hFile, pData, dwDataSize, &dwRet, NULL);
// 關(guān)閉句柄保存文件
CloseHandle(hFile);
return TRUE;
}
使用時(shí)調(diào)用HttpDownload
實(shí)現(xiàn)數(shù)據(jù)下載,下載后的文件會(huì)保存在pHttpDownloadData
中,此時(shí)直接調(diào)用SaveToFile
將其保存在文件中即可;
int main(int argc, char* argv[])
{
// 設(shè)置需要下載的地址
char szHttpDownloadUrl[] = "http://www.lyshark.com/index.html";
BYTE* pHttpDownloadData = NULL;
DWORD dwHttpDownloadDataSize = 0;
// HTTP下載
if (TRUE == HttpDownload(szHttpDownloadUrl, &pHttpDownloadData, &dwHttpDownloadDataSize))
{
std::cout << "已保存文件,長(zhǎng)度: " << dwHttpDownloadDataSize << " 字節(jié)"<< std::endl;
}
// 將下載數(shù)據(jù)保存成文件
SaveToFile((char *)"d://index.html", pHttpDownloadData, dwHttpDownloadDataSize);
// 釋放內(nèi)存
delete[]pHttpDownloadData;
pHttpDownloadData = NULL;
system("pause");
return 0;
}
運(yùn)行后則可一輸出響應(yīng)頭Content-Length:
完整參數(shù)以及輸出的字節(jié)數(shù),如下圖所示;
上傳文件內(nèi)容
服務(wù)端,首先需要實(shí)現(xiàn)一個(gè)簡(jiǎn)單的上傳接收功能,這里使用flask框架實(shí)現(xiàn),通過(guò)執(zhí)行pip install flask
命令安裝這個(gè)庫(kù),安裝成功以后手動(dòng)保存為main.py
文件,上傳文件是只需要向http://127.0.0.1/upload?file=
推送數(shù)據(jù)即可,代碼如下;
from flask import Flask, request
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload_file():
file = request.args.get('file')
if not file:
return "上傳錯(cuò)誤,文件名未指定"
try:
with open(file, 'wb') as uploaded_file:
uploaded_file.write(request.get_data())
if os.path.exists(file):
return "上傳成功"
else:
return "上傳失敗"
except Exception as e:
return f"上傳失敗: {str(e)}"
if __name__ == '__main__':
app.run(debug=True, host='localhost', port=80)
服務(wù)端以管理員身份運(yùn)行main.py
文件,此時(shí)會(huì)啟用一個(gè)Web服務(wù)器用于接收客戶端的上傳請(qǐng)求,如下圖所示;
接著是客戶端的實(shí)現(xiàn),首先介紹如下幾個(gè)關(guān)鍵API函數(shù);
HttpSendRequestEx
用于發(fā)送帶有附加選項(xiàng)的 HTTP 請(qǐng)求。相對(duì)于 HttpSendRequest
,它提供了更多的靈活性,允許在請(qǐng)求中包含額外的信息,例如頭部和數(shù)據(jù)。
以下是 HttpSendRequestEx
的原型:
BOOL HttpSendRequestEx(
HINTERNET hRequest,
LPINTERNET_BUFFERS lpBuffersIn,
LPINTERNET_BUFFERS lpBuffersOut,
DWORD dwFlags,
DWORD_PTR dwContext
);
參數(shù)說(shuō)明:
-
hRequest
:由HttpOpenRequest
返回的句柄,表示 HTTP 請(qǐng)求。 -
lpBuffersIn
:指向INTERNET_BUFFERS
結(jié)構(gòu)的指針,其中包含要作為請(qǐng)求的一部分發(fā)送的數(shù)據(jù)。 -
lpBuffersOut
:指向INTERNET_BUFFERS
結(jié)構(gòu)的指針,用于接收響應(yīng)中接收到的數(shù)據(jù)。 -
dwFlags
:附加標(biāo)志,控制函數(shù)的行為。這可以包括選項(xiàng),如INTERNET_FLAG_RELOAD
、INTERNET_FLAG_SECURE
等。 -
dwContext
:傳遞給回調(diào)函數(shù)的用戶定義的上下文值。
INTERNET_BUFFERS
是一個(gè)結(jié)構(gòu),允許您在 HTTP 請(qǐng)求和響應(yīng)中指定用于發(fā)送和接收數(shù)據(jù)的緩沖區(qū)。
使用 HttpSendRequestEx
需要謹(jǐn)慎處理內(nèi)存,并根據(jù)您的需求設(shè)置 INTERNET_BUFFERS
結(jié)構(gòu)的具體方式。
InternetWriteFile
用于將數(shù)據(jù)寫入到由 InternetOpenUrl
、InternetOpen
、HttpOpenRequest
或 FtpOpenFile
等函數(shù)打開的 URL、連接或文件。以下是該函數(shù)的原型:
BOOL InternetWriteFile(
HINTERNET hFile,
LPCVOID lpBuffer,
DWORD dwNumberOfBytesToWrite,
LPDWORD lpdwNumberOfBytesWritten
);
參數(shù)說(shuō)明:
-
hFile
: 調(diào)用InternetOpenUrl
、InternetOpen
、HttpOpenRequest
或FtpOpenFile
等函數(shù)返回的句柄,表示要寫入的文件、URL 或連接。 -
lpBuffer
: 指向包含要寫入的數(shù)據(jù)的緩沖區(qū)的指針。 -
dwNumberOfBytesToWrite
: 要寫入的字節(jié)數(shù)。 -
lpdwNumberOfBytesWritten
: 指向一個(gè)變量,表示實(shí)際寫入的字節(jié)數(shù)。在調(diào)用函數(shù)前,應(yīng)該將該變量設(shè)置為緩沖區(qū)的大小。在調(diào)用函數(shù)后,該變量將包含實(shí)際寫入的字節(jié)數(shù)。
返回值:
如果函數(shù)調(diào)用成功,返回非零值;如果函數(shù)調(diào)用失敗,返回零??梢允褂?GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
InternetWriteFile
主要用于將數(shù)據(jù)寫入網(wǎng)絡(luò)資源,如通過(guò) HTTP 或 FTP 協(xié)議上傳文件。在調(diào)用此函數(shù)之前,通常需要先調(diào)用其他相關(guān)的函數(shù),如 InternetOpenUrl
、InternetOpen
、HttpOpenRequest
等。同樣,使用完資源后,需要使用 InternetCloseHandle
函數(shù)關(guān)閉相應(yīng)的句柄,以釋放資源。
HttpEndRequest
它通常與 HttpSendRequest
或 HttpSendRequestEx
配合使用,用于完成 HTTP 請(qǐng)求的發(fā)送,并準(zhǔn)備接收服務(wù)器的響應(yīng)。
以下是 HttpEndRequest
函數(shù)的原型:
BOOL HttpEndRequest(
HINTERNET hRequest,
LPINTERNET_BUFFERS lpBuffersOut,
DWORD dwFlags,
DWORD_PTR dwContext
);
參數(shù)說(shuō)明:
-
hRequest
: 調(diào)用HttpOpenRequest
、HttpOpenRequestEx
、HttpSendRequest
或HttpSendRequestEx
等函數(shù)返回的 HTTP 請(qǐng)求句柄。 -
lpBuffersOut
: 指向一個(gè)INTERNET_BUFFERS
結(jié)構(gòu)的指針,該結(jié)構(gòu)用于傳遞關(guān)于響應(yīng)數(shù)據(jù)的信息??梢詾?NULL
。 -
dwFlags
: 一些標(biāo)志,用于指定結(jié)束請(qǐng)求的選項(xiàng)。通常為 0。 -
dwContext
: 用戶定義的應(yīng)用程序上下文,將在回調(diào)函數(shù)中使用。
返回值:
如果函數(shù)調(diào)用成功,返回非零值;如果函數(shù)調(diào)用失敗,返回零??梢允褂?GetLastError
函數(shù)獲取詳細(xì)的錯(cuò)誤信息。
HttpEndRequest
的主要作用是完成 HTTP 請(qǐng)求的發(fā)送,并在請(qǐng)求完成后準(zhǔn)備接收服務(wù)器的響應(yīng)。在調(diào)用此函數(shù)之后,通常會(huì)使用 InternetReadFile
函數(shù)等來(lái)讀取服務(wù)器的響應(yīng)數(shù)據(jù)。
上傳文件的完整代碼是這樣的,如下所示;
#include <iostream>
#include <Windows.h>
#include <WinInet.h>
#pragma comment(lib, "WinInet.lib")
using namespace std;
// 切割路徑
BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, DWORD dwBufferSize)
{
BOOL bRet = FALSE;
URL_COMPONENTS uc = { 0 };
// 初始化變量中的內(nèi)容
RtlZeroMemory(&uc, sizeof(uc));
RtlZeroMemory(pszScheme, dwBufferSize);
RtlZeroMemory(pszHostName, dwBufferSize);
RtlZeroMemory(pszUserName, dwBufferSize);
RtlZeroMemory(pszPassword, dwBufferSize);
RtlZeroMemory(pszUrlPath, dwBufferSize);
RtlZeroMemory(pszExtraInfo, dwBufferSize);
// 將長(zhǎng)度填充到結(jié)構(gòu)中
uc.dwStructSize = sizeof(uc);
uc.dwSchemeLength = dwBufferSize - 1;
uc.dwHostNameLength = dwBufferSize - 1;
uc.dwUserNameLength = dwBufferSize - 1;
uc.dwPasswordLength = dwBufferSize - 1;
uc.dwUrlPathLength = dwBufferSize - 1;
uc.dwExtraInfoLength = dwBufferSize - 1;
uc.lpszScheme = pszScheme;
uc.lpszHostName = pszHostName;
uc.lpszUserName = pszUserName;
uc.lpszPassword = pszPassword;
uc.lpszUrlPath = pszUrlPath;
uc.lpszExtraInfo = pszExtraInfo;
// 分解URL地址
bRet = InternetCrackUrl(pszUrl, 0, 0, &uc);
if (FALSE == bRet)
{
return bRet;
}
return bRet;
}
// 從響應(yīng)信息頭信息中獲取數(shù)據(jù)內(nèi)容長(zhǎng)度大小
BOOL GetContentLength(char* pResponseHeader, DWORD* pdwContentLength)
{
int i = 0;
char szContentLength[MAX_PATH] = { 0 };
DWORD dwContentLength = 0;
char szSubStr[] = "Content-Length: ";
RtlZeroMemory(szContentLength, MAX_PATH);
// 在傳入字符串中查找子串
char* p = strstr(pResponseHeader, szSubStr);
if (NULL == p)
{
return FALSE;
}
p = p + lstrlen(szSubStr);
// 如果找到了就提取出里面的純數(shù)字
while (('0' <= *p) && ('9' >= *p))
{
szContentLength[i] = *p;
p++;
i++;
}
// 字符串轉(zhuǎn)數(shù)字
dwContentLength = atoi(szContentLength);
*pdwContentLength = dwContentLength;
return TRUE;
}
// 數(shù)據(jù)上傳
BOOL HttpUpload(char* pszUploadUrl, BYTE* pUploadData, DWORD dwUploadDataSize)
{
// 初始化變量中的內(nèi)容
char szScheme[MAX_PATH] = { 0 };
char szHostName[MAX_PATH] = { 0 };
char szUserName[MAX_PATH] = { 0 };
char szPassword[MAX_PATH] = { 0 };
char szUrlPath[MAX_PATH] = { 0 };
char szExtraInfo[MAX_PATH] = { 0 };
// 將長(zhǎng)度填充到結(jié)構(gòu)中
RtlZeroMemory(szScheme, MAX_PATH);
RtlZeroMemory(szHostName, MAX_PATH);
RtlZeroMemory(szUserName, MAX_PATH);
RtlZeroMemory(szPassword, MAX_PATH);
RtlZeroMemory(szUrlPath, MAX_PATH);
RtlZeroMemory(szExtraInfo, MAX_PATH);
// 分解URL地址
if (FALSE == UrlCrack(pszUploadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))
{
return FALSE;
}
// 數(shù)據(jù)上傳
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
DWORD dwOpenRequestFlags = 0;
BOOL bRet = FALSE;
DWORD dwRet = 0;
unsigned char* pResponseHeaderIInfo = NULL;
DWORD dwResponseHeaderIInfoSize = 2048;
BYTE* pBuf = NULL;
DWORD dwBufSize = 64 * 1024;
BYTE* pResponseBodyData = NULL;
DWORD dwResponseBodyDataSize = 0;
DWORD dwOffset = 0;
DWORD dwPostDataSize = dwUploadDataSize;
INTERNET_BUFFERS internetBuffers = { 0 };
do
{
// 建立會(huì)話
hInternet = InternetOpen("WinInetPost/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (NULL == hInternet)
{
break;
}
// 建立連接
hConnect = InternetConnect(hInternet, szHostName, INTERNET_DEFAULT_HTTP_PORT, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);
if (NULL == hConnect)
{
break;
}
// 打開并發(fā)送HTTP請(qǐng)求
dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_AUTH |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD;
if (0 < lstrlen(szExtraInfo))
{
lstrcat(szUrlPath, szExtraInfo);
}
// 使用POST請(qǐng)求
hRequest = HttpOpenRequest(hConnect, "POST", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
if (NULL == hRequest)
{
break;
}
// 告訴服務(wù)器傳輸數(shù)據(jù)的總大小
RtlZeroMemory(&internetBuffers, sizeof(internetBuffers));
internetBuffers.dwStructSize = sizeof(internetBuffers);
internetBuffers.dwBufferTotal = dwPostDataSize;
bRet = HttpSendRequestEx(hRequest, &internetBuffers, NULL, 0, 0);
if (FALSE == bRet)
{
break;
}
// 發(fā)送數(shù)據(jù)
bRet = InternetWriteFile(hRequest, pUploadData, dwUploadDataSize, &dwRet);
if (FALSE == bRet)
{
break;
}
// 發(fā)送完畢
bRet = HttpEndRequest(hRequest, NULL, 0, 0);
if (FALSE == bRet)
{
break;
}
// 接收響應(yīng)報(bào)文
pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize];
if (NULL == pResponseHeaderIInfo)
{
break;
}
RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);
bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);
if (FALSE == bRet)
{
break;
}
// 獲取數(shù)據(jù)長(zhǎng)度
bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwResponseBodyDataSize);
if (FALSE == bRet)
{
break;
}
// 輸出響應(yīng)頭
std::cout << pResponseHeaderIInfo << std::endl;
// 接收?qǐng)?bào)文主體內(nèi)容(Get Response Body)
pBuf = new BYTE[dwBufSize];
if (NULL == pBuf)
{
break;
}
pResponseBodyData = new BYTE[dwResponseBodyDataSize];
if (NULL == pResponseBodyData)
{
break;
}
RtlZeroMemory(pResponseBodyData, dwResponseBodyDataSize);
do
{
// 循環(huán)讀取數(shù)據(jù)并填充到緩沖區(qū)內(nèi)
RtlZeroMemory(pBuf, dwBufSize);
bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);
if (FALSE == bRet)
{
break;
}
RtlCopyMemory((pResponseBodyData + dwOffset), pBuf, dwRet);
dwOffset = dwOffset + dwRet;
} while (dwResponseBodyDataSize > dwOffset);
} while (FALSE);
// 關(guān)閉釋放
if (NULL != pResponseBodyData)
{
delete[]pResponseBodyData;
pResponseBodyData = NULL;
}
if (NULL != pBuf)
{
delete[]pBuf;
pBuf = NULL;
}
if (NULL != pResponseHeaderIInfo)
{
delete[]pResponseHeaderIInfo;
pResponseHeaderIInfo = NULL;
}
if (NULL != hRequest)
{
InternetCloseHandle(hRequest);
hRequest = NULL;
}
if (NULL != hConnect)
{
InternetCloseHandle(hConnect);
hConnect = NULL;
}
if (NULL != hInternet)
{
InternetCloseHandle(hInternet);
hInternet = NULL;
}
return bRet;
}
上傳代碼通過(guò)指定szHttpUploadUrl
將d://lyshark.exe
文件提交到遠(yuǎn)程主機(jī),代碼如下所示;
int main(int argc, char* argv[])
{
// 設(shè)置上傳接口地址
char szHttpUploadUrl[] = "http://127.0.0.1/upload?file=lyshark.exe";
// 被上傳文件絕對(duì)路徑
char szHttpUploadFileName[] = "d://lyshark.exe";
BYTE* pHttpUploadData = NULL;
DWORD dwHttpUploadDataSize = 0;
DWORD dwRet = 0;
// 打開文件
HANDLE hFile = CreateFile(szHttpUploadFileName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_ARCHIVE, NULL);
// 獲取文件大小
dwHttpUploadDataSize = GetFileSize(hFile, NULL);
// 讀取文件數(shù)據(jù)
pHttpUploadData = new BYTE[dwHttpUploadDataSize];
ReadFile(hFile, pHttpUploadData, dwHttpUploadDataSize, &dwRet, NULL);
dwHttpUploadDataSize = dwRet;
// 上傳數(shù)據(jù)
if (FALSE == HttpUpload(szHttpUploadUrl, pHttpUploadData, dwHttpUploadDataSize))
{
return 0;
}
// 釋放內(nèi)存
delete[]pHttpUploadData;
pHttpUploadData = NULL;
CloseHandle(hFile);
system("pause");
return 0;
}
運(yùn)行后提交文件,則可以看到輸出信息,如下圖所示;文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-826964.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-826964.html
到了這里,關(guān)于C/C++ 通過(guò)HTTP實(shí)現(xiàn)文件上傳下載的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!