WinInet(Windows Internet)是 Microsoft Windows 操作系统中的一个 API 集,用于提供对 Internet 相关功能的支持。它包括了一系列的函数,使得 Windows 应用程序能够进行网络通信、处理 HTTP 请求、FTP 操作等。WinInet 提供了一套完整的网络通信工具,使得开发者能够轻松地构建支持网络功能的应用程序,涵盖了从简单的 HTTP 请求到复杂的文件传输等多种网络操作。

分解URL地址

InternetCrackUrl 函数可实现对URL字符串进行解析,提取其中的协议、主机名、端口、路径和其他信息,并将这些信息存储在 URL_COMPONENTS 结构中,方便后续的网络操作,该函数是Windows下默认提供的,函数与依赖结果如下所示;

函数原型

BOOL InternetCrackUrl(
LPCTSTR lpszUrl,
DWORD dwUrlLength,
DWORD dwFlags,
LPURL_COMPONENTS lpUrlComponents
);

参数说明

  • lpszUrl:指定待解析的 URL 字符串。
  • dwUrlLength:指定 URL 字符串的长度。
  • dwFlags:指定解析 URL 的标志,可以是以下值之一:
    • ICU_DECODE:对 URL 进行解码。
    • ICU_ESCAPE:对 URL 进行转义。
  • lpUrlComponents:一个指向 URL_COMPONENTS 结构的指针,用于存储解析后的各个部分信息。

URL_COMPONENTS结构

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;

返回值

如果函数成功,返回 TRUE,并在 lpUrlComponents 结构中存储解析后的信息;如果失败,返回 FALSE。在失败时,可以调用 GetLastError 函数获取详细的错误信息。

函数调用

#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 }; // 初始化变量中的内容
RtlZeroMemory(&uc, sizeof(uc));
RtlZeroMemory(pszScheme, dwBufferSize);
RtlZeroMemory(pszHostName, dwBufferSize);
RtlZeroMemory(pszUserName, dwBufferSize);
RtlZeroMemory(pszPassword, dwBufferSize);
RtlZeroMemory(pszUrlPath, dwBufferSize);
RtlZeroMemory(pszExtraInfo, dwBufferSize); // 将长度填充到结构中
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"; // 对应的变量
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;
}

运行代码输出特定网址的每个部分,如下图所示;

下载页面内容

InternetOpen

用于初始化 WinINet 函数的使用。以下是该函数的原型:

HINTERNET InternetOpen(
LPCWSTR lpszAgent,
DWORD dwAccessType,
LPCWSTR lpszProxyName,
LPCWSTR lpszProxyBypass,
DWORD dwFlags
);

参数说明:

  • lpszAgent: 指定应用程序的名称,用于标识调用 InternetOpen 的应用程序。
  • dwAccessType: 指定访问类型,可以是 INTERNET_OPEN_TYPE_DIRECTINTERNET_OPEN_TYPE_PROXYINTERNET_OPEN_TYPE_PRECONFIG 中的一个。
  • lpszProxyName: 如果 dwAccessTypeINTERNET_OPEN_TYPE_PROXY,则指定代理服务器的名称。否则,可以设为 NULL
  • lpszProxyBypass: 如果 dwAccessTypeINTERNET_OPEN_TYPE_PROXY,则指定绕过代理服务器的地址。否则,可以设为 NULL
  • dwFlags: 一些标志,可以用来指定额外的行为,如 INTERNET_FLAG_ASYNC 用于异步操作。

返回值:

如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,用于后续的 WinINet 操作。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。

InternetConnect

用于建立到远程服务器的连接。以下是该函数的原型:

HINTERNET InternetConnect(
HINTERNET hInternet,
LPCWSTR lpszServerName,
INTERNET_PORT nServerPort,
LPCWSTR lpszUserName,
LPCWSTR lpszPassword,
DWORD dwService,
DWORD dwFlags,
DWORD_PTR dwContext
);

参数说明:

  • hInternet: 调用 InternetOpen 返回的句柄,表示连接的上下文。
  • lpszServerName: 要连接的服务器的名称或 IP 地址。
  • nServerPort: 服务器的端口号。
  • lpszUserName: 连接服务器时要使用的用户名,可以为 NULL
  • lpszPassword: 连接服务器时要使用的密码,可以为 NULL
  • dwService: 指定服务类型,可以是 INTERNET_SERVICE_FTPINTERNET_SERVICE_HTTP 或其他服务类型。
  • dwFlags: 一些标志,用于指定连接的属性,如 INTERNET_FLAG_RELOADINTERNET_FLAG_SECURE 等。
  • dwContext: 用户定义的应用程序上下文,将在回调函数中使用。

返回值:

如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,表示连接的上下文。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。

InternetConnect 用于建立连接后,可以使用返回的句柄执行相关的协议操作,如 FTP 或 HTTP 操作。使用完连接后,同样需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpOpenRequest

它是在使用 WinINet 库进行 HTTP 操作时的一部分。以下是该函数的原型:

HINTERNET HttpOpenRequest(
HINTERNET hConnect,
LPCWSTR lpszVerb,
LPCWSTR lpszObjectName,
LPCWSTR lpszVersion,
LPCWSTR lpszReferrer,
LPCWSTR *lplpszAcceptTypes,
DWORD dwFlags,
DWORD_PTR dwContext
);

参数说明:

  • hConnect: 调用 InternetConnect 返回的连接句柄,表示请求的上下文。
  • lpszVerb: HTTP 请求方法,如 "GET"、"POST" 等。
  • lpszObjectName: 请求的对象名,通常是 URL 的路径部分。
  • lpszVersion: HTTP 协议版本,通常是 "HTTP/1.1"。
  • lpszReferrer: 引用的来源,可以为 NULL
  • lplpszAcceptTypes: 指定可接受的媒体类型,可以为 NULL
  • dwFlags: 一些标志,用于指定请求的属性,如 INTERNET_FLAG_RELOADINTERNET_FLAG_SECURE 等。
  • dwContext: 用户定义的应用程序上下文,将在回调函数中使用。

返回值:

如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,表示打开的 HTTP 请求。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。

一旦打开了 HTTP 请求,可以使用返回的句柄执行发送请求、接收响应等操作。使用完请求后,同样需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpSendRequest

用于发送 HTTP 请求的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:

BOOL HttpSendRequest(
HINTERNET hRequest,
LPCWSTR lpszHeaders,
DWORD dwHeadersLength,
LPVOID lpOptional,
DWORD dwOptionalLength
);

参数说明:

  • hRequest: 调用 HttpOpenRequest 返回的 HTTP 请求句柄,表示要发送请求的上下文。
  • lpszHeaders: 包含请求头信息的字符串,可以为 NULL
  • dwHeadersLength: 请求头的长度,如果 lpszHeadersNULL,则可以为零。
  • lpOptional: 包含请求的可选数据的缓冲区,可以为 NULL
  • dwOptionalLength: 可选数据的长度,如果 lpOptionalNULL,则可以为零。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

HttpSendRequest 用于实际发送 HTTP 请求。在调用此函数之后,可以使用其他 WinINet 函数来读取服务器的响应。同样,使用完请求后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpQueryInfo

用于检索有关 HTTP 请求或响应的信息的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:

BOOL HttpQueryInfo(
HINTERNET hRequest,
DWORD dwInfoLevel,
LPVOID lpBuffer,
LPDWORD lpdwBufferLength,
LPDWORD lpdwIndex
);

参数说明:

  • hRequest: 调用 HttpOpenRequest 返回的 HTTP 请求句柄,表示要查询信息的上下文。
  • dwInfoLevel: 指定要检索的信息类型,可以是预定义的常量,如 HTTP_QUERY_STATUS_CODEHTTP_QUERY_CONTENT_TYPE 等。
  • lpBuffer: 用于接收检索到的信息的缓冲区。
  • lpdwBufferLength: 指向一个变量,表示 lpBuffer 缓冲区的大小。在调用函数前,应该将该变量设置为 lpBuffer 缓冲区的大小。在调用函数后,该变量将包含实际写入缓冲区的字节数。
  • lpdwIndex: 如果请求返回多个值,可以使用此参数指定要检索的值的索引。对于单值的信息,可以将其设置为 NULL

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

HttpQueryInfo 用于获取与 HTTP 请求或响应相关的信息,如状态码、内容类型等。注意,在调用此函数之前,通常需要先调用 HttpSendRequest 发送请求。同样,使用完请求后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

InternetReadFile

用于从指定的句柄读取数据的函数,通常在使用 WinINet 库进行网络操作时调用。以下是该函数的原型:

BOOL InternetReadFile(
HINTERNET hFile,
LPVOID lpBuffer,
DWORD dwNumberOfBytesToRead,
LPDWORD lpdwNumberOfBytesRead
);

参数说明:

  • hFile: 调用 HttpOpenRequestFtpOpenFile 返回的句柄,表示要读取数据的上下文。
  • lpBuffer: 用于接收读取到的数据的缓冲区。
  • dwNumberOfBytesToRead: 指定要读取的字节数。
  • lpdwNumberOfBytesRead: 指向一个变量,表示 lpBuffer 缓冲区中实际读取的字节数。在调用函数前,应该将该变量设置为 lpBuffer 缓冲区的大小。在调用函数后,该变量将包含实际读取的字节数。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

InternetReadFile 用于从网络资源中读取数据,如从 HTTP 请求的响应中读取内容。在调用此函数之前,通常需要先调用其他相关的函数,如 HttpOpenRequestHttpSendRequestHttpQueryInfo。同样,使用完资源后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

下载页面的完整代码是这样的,如下所示;

#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 }; // 初始化变量中的内容
RtlZeroMemory(&uc, sizeof(uc));
RtlZeroMemory(pszScheme, dwBufferSize);
RtlZeroMemory(pszHostName, dwBufferSize);
RtlZeroMemory(pszUserName, dwBufferSize);
RtlZeroMemory(pszPassword, dwBufferSize);
RtlZeroMemory(pszUrlPath, dwBufferSize);
RtlZeroMemory(pszExtraInfo, dwBufferSize); // 将长度填充到结构中
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;
} // 从响应信息头信息中获取数据内容长度大小
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); // 如果找到了就提取出里面的纯数字
while (('0' <= *p) && ('9' >= *p))
{
szContentLength[i] = *p;
p++;
i++;
} // 字符串转数字
dwContentLength = atoi(szContentLength);
*pdwContentLength = dwContentLength;
return TRUE;
} // 数据下载
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;
} // 数据下载
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
{
// 建立会话
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;
} // 打开并发送HTTP请求
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模式打开请求
hRequest = HttpOpenRequest(hConnect, "GET", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
if (NULL == hRequest)
{
break;
} // 发送请求
bRet = HttpSendRequest(hRequest, NULL, 0, NULL, 0);
if (FALSE == bRet)
{
break;
}
// 接收响应的报文信息头(Get Response Header)
pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize];
if (NULL == pResponseHeaderIInfo)
{
break;
}
RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize); // 查询HTTP请求头
bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);
if (FALSE == bRet)
{
break;
} // 从字符串中 "Content-Length: " 网页获取数据长度
bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwDownloadDataSize); // 输出完整响应头
std::cout << pResponseHeaderIInfo << std::endl;
if (FALSE == bRet)
{
break;
}
// 接收报文主体内容(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); // 循环读入数据并保存在变量中
bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);
if (FALSE == bRet)
{
break;
} RtlCopyMemory((pDownloadData + dwOffset), pBuf, dwRet);
dwOffset = dwOffset + dwRet; } while (dwDownloadDataSize > dwOffset); // 返回数据
*ppDownloadData = pDownloadData;
*pdwDownloadDataSize = dwDownloadDataSize; } while (FALSE); // 关闭并释放资源
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;
} // 创建并保存文件
BOOL SaveToFile(char* pszFileName, BYTE* pData, DWORD dwDataSize)
{
// 创建空文件
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; // 写出数据到文件
WriteFile(hFile, pData, dwDataSize, &dwRet, NULL); // 关闭句柄保存文件
CloseHandle(hFile); return TRUE;
}

使用时调用HttpDownload实现数据下载,下载后的文件会保存在pHttpDownloadData中,此时直接调用SaveToFile将其保存在文件中即可;

int main(int argc, char* argv[])
{
// 设置需要下载的地址
char szHttpDownloadUrl[] = "http://www.lyshark.com/index.html";
BYTE* pHttpDownloadData = NULL;
DWORD dwHttpDownloadDataSize = 0; // HTTP下载
if (TRUE == HttpDownload(szHttpDownloadUrl, &pHttpDownloadData, &dwHttpDownloadDataSize))
{
std::cout << "已保存文件,长度: " << dwHttpDownloadDataSize << " 字节"<< std::endl;
} // 将下载数据保存成文件
SaveToFile((char *)"d://index.html", pHttpDownloadData, dwHttpDownloadDataSize); // 释放内存
delete[]pHttpDownloadData;
pHttpDownloadData = NULL; system("pause");
return 0;
}

运行后则可一输出响应头Content-Length:完整参数以及输出的字节数,如下图所示;

上传文件内容

服务端,首先需要实现一个简单的上传接收功能,这里使用flask框架实现,通过执行pip install flask命令安装这个库,安装成功以后手动保存为main.py文件,上传文件是只需要向http://127.0.0.1/upload?file=推送数据即可,代码如下;

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 "上传错误,文件名未指定" 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)

服务端以管理员身份运行main.py文件,此时会启用一个Web服务器用于接收客户端的上传请求,如下图所示;

接着是客户端的实现,首先介绍如下几个关键API函数;

HttpSendRequestEx

用于发送带有附加选项的 HTTP 请求。相对于 HttpSendRequest,它提供了更多的灵活性,允许在请求中包含额外的信息,例如头部和数据。

以下是 HttpSendRequestEx 的原型:

BOOL HttpSendRequestEx(
HINTERNET hRequest,
LPINTERNET_BUFFERS lpBuffersIn,
LPINTERNET_BUFFERS lpBuffersOut,
DWORD dwFlags,
DWORD_PTR dwContext
);

参数说明:

  • hRequest:由 HttpOpenRequest 返回的句柄,表示 HTTP 请求。
  • lpBuffersIn:指向 INTERNET_BUFFERS 结构的指针,其中包含要作为请求的一部分发送的数据。
  • lpBuffersOut:指向 INTERNET_BUFFERS 结构的指针,用于接收响应中接收到的数据。
  • dwFlags:附加标志,控制函数的行为。这可以包括选项,如 INTERNET_FLAG_RELOADINTERNET_FLAG_SECURE 等。
  • dwContext:传递给回调函数的用户定义的上下文值。

INTERNET_BUFFERS 是一个结构,允许您在 HTTP 请求和响应中指定用于发送和接收数据的缓冲区。

使用 HttpSendRequestEx 需要谨慎处理内存,并根据您的需求设置 INTERNET_BUFFERS 结构的具体方式。

InternetWriteFile

用于将数据写入到由 InternetOpenUrlInternetOpenHttpOpenRequestFtpOpenFile 等函数打开的 URL、连接或文件。以下是该函数的原型:

BOOL InternetWriteFile(
HINTERNET hFile,
LPCVOID lpBuffer,
DWORD dwNumberOfBytesToWrite,
LPDWORD lpdwNumberOfBytesWritten
);

参数说明:

  • hFile: 调用 InternetOpenUrlInternetOpenHttpOpenRequestFtpOpenFile 等函数返回的句柄,表示要写入的文件、URL 或连接。
  • lpBuffer: 指向包含要写入的数据的缓冲区的指针。
  • dwNumberOfBytesToWrite: 要写入的字节数。
  • lpdwNumberOfBytesWritten: 指向一个变量,表示实际写入的字节数。在调用函数前,应该将该变量设置为缓冲区的大小。在调用函数后,该变量将包含实际写入的字节数。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

InternetWriteFile 主要用于将数据写入网络资源,如通过 HTTP 或 FTP 协议上传文件。在调用此函数之前,通常需要先调用其他相关的函数,如 InternetOpenUrlInternetOpenHttpOpenRequest 等。同样,使用完资源后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpEndRequest

它通常与 HttpSendRequestHttpSendRequestEx 配合使用,用于完成 HTTP 请求的发送,并准备接收服务器的响应。

以下是 HttpEndRequest 函数的原型:

BOOL HttpEndRequest(
HINTERNET hRequest,
LPINTERNET_BUFFERS lpBuffersOut,
DWORD dwFlags,
DWORD_PTR dwContext
);

参数说明:

  • hRequest: 调用 HttpOpenRequestHttpOpenRequestExHttpSendRequestHttpSendRequestEx 等函数返回的 HTTP 请求句柄。
  • lpBuffersOut: 指向一个 INTERNET_BUFFERS 结构的指针,该结构用于传递关于响应数据的信息。可以为 NULL
  • dwFlags: 一些标志,用于指定结束请求的选项。通常为 0。
  • dwContext: 用户定义的应用程序上下文,将在回调函数中使用。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

HttpEndRequest 的主要作用是完成 HTTP 请求的发送,并在请求完成后准备接收服务器的响应。在调用此函数之后,通常会使用 InternetReadFile 函数等来读取服务器的响应数据。

上传文件的完整代码是这样的,如下所示;

#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 }; // 初始化变量中的内容
RtlZeroMemory(&uc, sizeof(uc));
RtlZeroMemory(pszScheme, dwBufferSize);
RtlZeroMemory(pszHostName, dwBufferSize);
RtlZeroMemory(pszUserName, dwBufferSize);
RtlZeroMemory(pszPassword, dwBufferSize);
RtlZeroMemory(pszUrlPath, dwBufferSize);
RtlZeroMemory(pszExtraInfo, dwBufferSize); // 将长度填充到结构中
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;
} // 从响应信息头信息中获取数据内容长度大小
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); // 如果找到了就提取出里面的纯数字
while (('0' <= *p) && ('9' >= *p))
{
szContentLength[i] = *p;
p++;
i++;
} // 字符串转数字
dwContentLength = atoi(szContentLength);
*pdwContentLength = dwContentLength;
return TRUE;
} // 数据上传
BOOL HttpUpload(char* pszUploadUrl, BYTE* pUploadData, DWORD dwUploadDataSize)
{
// 初始化变量中的内容
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(pszUploadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))
{
return FALSE;
} // 数据上传
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
{
// 建立会话
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;
} // 打开并发送HTTP请求
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请求
hRequest = HttpOpenRequest(hConnect, "POST", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
if (NULL == hRequest)
{
break;
} // 告诉服务器传输数据的总大小
RtlZeroMemory(&internetBuffers, sizeof(internetBuffers));
internetBuffers.dwStructSize = sizeof(internetBuffers);
internetBuffers.dwBufferTotal = dwPostDataSize; bRet = HttpSendRequestEx(hRequest, &internetBuffers, NULL, 0, 0);
if (FALSE == bRet)
{
break;
} // 发送数据
bRet = InternetWriteFile(hRequest, pUploadData, dwUploadDataSize, &dwRet);
if (FALSE == bRet)
{
break;
} // 发送完毕
bRet = HttpEndRequest(hRequest, NULL, 0, 0);
if (FALSE == bRet)
{
break;
} // 接收响应报文
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;
} // 获取数据长度
bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwResponseBodyDataSize);
if (FALSE == bRet)
{
break;
} // 输出响应头
std::cout << pResponseHeaderIInfo << std::endl; // 接收报文主体内容(Get Response Body)
pBuf = new BYTE[dwBufSize];
if (NULL == pBuf)
{
break;
}
pResponseBodyData = new BYTE[dwResponseBodyDataSize];
if (NULL == pResponseBodyData)
{
break;
}
RtlZeroMemory(pResponseBodyData, dwResponseBodyDataSize); do
{
// 循环读取数据并填充到缓冲区内
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); // 关闭释放
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;
}

上传代码通过指定szHttpUploadUrld://lyshark.exe文件提交到远程主机,代码如下所示;

int main(int argc, char* argv[])
{
// 设置上传接口地址
char szHttpUploadUrl[] = "http://127.0.0.1/upload?file=lyshark.exe"; // 被上传文件绝对路径
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); // 读取文件数据
pHttpUploadData = new BYTE[dwHttpUploadDataSize];
ReadFile(hFile, pHttpUploadData, dwHttpUploadDataSize, &dwRet, NULL);
dwHttpUploadDataSize = dwRet; // 上传数据
if (FALSE == HttpUpload(szHttpUploadUrl, pHttpUploadData, dwHttpUploadDataSize))
{
return 0;
} // 释放内存
delete[]pHttpUploadData;
pHttpUploadData = NULL;
CloseHandle(hFile); system("pause");
return 0;
}

运行后提交文件,则可以看到输出信息,如下图所示;

C/C++ 通过HTTP实现文件上传下载的更多相关文章

  1. Struts的文件上传下载

    Struts的文件上传下载 1.文件上传 Struts2的文件上传也是使用fileUpload的组件,这个组默认是集合在框架里面的.且是使用拦截器:<interceptor name=" ...

  2. Android okHttp网络请求之文件上传下载

    前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...

  3. Selenium2学习-039-WebUI自动化实战实例-文件上传下载

    通常在 WebUI 自动化测试过程中必然会涉及到文件上传的自动化测试需求,而开发在进行相应的技术实现是不同的,粗略可划分为两类:input标签类(类型为file)和非input标签类(例如:div.a ...

  4. 艺萌文件上传下载及自动更新系统(基于networkComms开源TCP通信框架)

    1.艺萌文件上传下载及自动更新系统,基于Winform技术,采用CS架构,开发工具为vs2010,.net2.0版本(可以很容易升级为3.5和4.0版本)开发语言c#. 本系统主要帮助客户学习基于TC ...

  5. 艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输)(一)

    艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输) 该系统基于开源的networkComms通讯框架,此通讯框架以前是收费的,目前已经免费并开元,作者是英国的,开发时间5年多,框架很稳定. 项 ...

  6. ssh框架文件上传下载

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. SpringMVC——返回JSON数据&&文件上传下载

    --------------------------------------------返回JSON数据------------------------------------------------ ...

  8. 【FTP】FTP文件上传下载-支持断点续传

    Jar包:apache的commons-net包: 支持断点续传 支持进度监控(有时出不来,搞不清原因) 相关知识点 编码格式: UTF-8等; 文件类型: 包括[BINARY_FILE_TYPE(常 ...

  9. NetworkComms 文件上传下载和客户端自动升级(非开源)

    演示程序下载地址:http://pan.baidu.com/s/1geVfmcr 淘宝地址:https://shop183793329.taobao.com 联系QQ号:3201175853 许可:购 ...

  10. SpringMVC文件上传下载

    在Spring MVC的基础框架搭建起来后,我们测试了spring mvc中的返回值类型,如果你还没有搭建好springmvc的架构请参考博文->http://www.cnblogs.com/q ...

随机推荐

  1. 如何安装和使用 Hugging Face Unity API

    Hugging Face Unity API 提供了一个简单易用的接口,允许开发者在自己的 Unity 项目中方便地访问和使用 Hugging Face AI 模型,已集成到 Hugging Face ...

  2. IntelliJ IDEA 2024年最新下载、安装使用教程、亲测可用

    本文讲的是2023.3最新版本IntelliJ IDEA破解.IntelliJ IDEA激活码.IntelliJ IDEA安装.IntelliJ IDEA永久激活码的最新永久激活教程,本文有mac和w ...

  3. OOALV 分割屏幕

    1功能说明 需要开发一个报表,分为上下两个部分,下边需要再分割为左右两个部分,点击上边部分的行,下边两个报表信息发生变化. 效果如下: 2代码实现 1.数据查询 分别查询MARA.MARC.MAKT三 ...

  4. Spring 学习笔记(1)文章导读

    <Spring 学习笔记>系列文章是博主在学习过 Spring 后对其进行总结的入门系列博文,适合初入 Spring 的小白,如果你最近正在学习或者打算学习 Spring 的话,不妨随着本 ...

  5. AtCoder Beginner Contest 167 (A~F,DEF Good)

    比赛链接:https://atcoder.jp/contests/abc167/tasks AB水题, C - Skill Up 题意: 初始时 \(m\) 个算法的能力均为 \(0\),\(n\) ...

  6. CH0304 IncDec Sequence (差分)

    题目链接: https://ac.nowcoder.com/acm/contest/999/B 思路:(见图中解释) AC代码: #include<bits/stdc++.h> using ...

  7. 企业如何利用 Serverless 快速扩展业务系统?

    2022 年 9 月 24 日,阿里云用户组(AUG)第 12 期活动在厦门举办.活动现场,阿里云高级技术专家史明伟(花名:世如)向参会企业代表分享了<未来已来--从技术升级到降本提效>. ...

  8. Synchronized的使用及原理总结

    本文为博主原创,未经允许不得转载 Synchronized的使用总结:   1.作用 原理 synchronized 的锁膨胀升级过程 对象的内存布局 锁的消除及逃逸分析 synchronized的方 ...

  9. HashMap集合遍历随机性问题分析

    一.原因分析 1.1 HashMap对象的遍历 HashMap的遍历是通过此类中字段table数组进行顺序遍历,原因如下所示: 1 #HashMap 迭代遍历源码 2 public final boo ...

  10. Linux复习笔记

    Linux复习笔记 常识说明 目录结构 Linux以树型结构管理文件,其最上层文件夹为 / ,也就是根目录. 如图所示,图中展示了一部分文件夹的结构: 所有的文件夹都属于根目录的子文件夹. 安装好系统 ...