C/C++ 通过HTTP实现文件上传下载

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);// 分解URLif (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, requestapp = 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;
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/207614.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R语言期末考试复习二

上篇文章的后续&#xff01;&#xff01;&#xff01;&#xff01; http://t.csdnimg.cn/sqvYD 1.给向量vec1设置名为"A","B","C","D","E","F","G"。 2.将矩阵mat1的行名设置为"Row1"&#…

powershell获取微软o365 21v日志

0x00 背景 o365 21v为o365的大陆版本&#xff0c;主要给国内用户使用。微软提供了powershell工具和接口获取云上日志。微软o365国内的代理目前是世纪互联。本文介绍如何用powershell和配置证书拉取云上日志。 0x01 实践 第一步&#xff0c;ip权限开通&#xff1a; 由世纪互联…

SQL Server 2016(为数据表Porducts添加数据)

1、实验环境。 某公司有一台已经安装了SQL Server 2016的服务器&#xff0c;并已经创建了数据库PM。 2、需求描述。 在数据库PM中创建表products&#xff0c;"编号"列的值自动增长并为主键。然后使用T-SQL语句为表格插入如下数据。 3、实验步骤。 1、使用SSMS管理工…

网络安全应急响应-Server2228(环境+解析)

网络安全应急响应 任务环境说明: 服务器场景:Server2228(开放链接)用户名:root,密码:p@ssw0rd123

【爬虫逆向分析实战】某笔登录算法分析——本地替换分析法

前言 作者最近在做一个收集粉币的项目&#xff0c;可以用来干嘛这里就不展开了&#x1f601;&#xff0c;需要进行登录换算token从而达到监控收集的作用&#xff0c;手机抓包发现他是通过APP进行计算之后再请求接口的&#xff0c;通过官网分析可能要比APP逆向方便多&#xff0…

如何解决SSL证书部署后未生效或网站显示不安全

本文介绍SSL证书部署后未生效或网站显示不安全的排查方法。 浏览器提示“您与此网站建立的连接不安全” 浏览器提示“无法访问此页面” 浏览器提示“这可能是因为站点使用过期或者不全的TLS安全设置” 浏览器提示“此页面上部分内容不安全&#xff08;例如图像&#xff09;”…

Python datetime 字符串 相互转 datetime

字符串转 datetime from datetime import datetime# 定义要转换的日期时间字符串 dt_str "2021-09-30 15:48:36"# 使用datetime.strptime()函数进行转换 dt_obj datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S") print(dt_obj)datetime 转字符串 from …

c语言练习13周(1~5)

输入任意整数n求以下公式和的平方根。 读取一系列的整数 X&#xff0c;对于每个 X&#xff0c;输出一个 1,2,…,X 的序列。 编写double fun(int a[M][M])函数&#xff0c;返回二维数组周边元素的平均值&#xff0c;M为定义好的符号常量。 编写double fun(int a[M])函…

Spinnaker 基于 jenkins 触发部署

jenkins job 触发部署 将 Jenkins 设置为 Spinnaker 中的持续集成 (CI) 系统可让您使用 Jenkins 触发管道、向管道添加 Jenkins 阶段或向管道添加脚本阶段。 前置要求&#xff1a; 已在kubernetes中部署spinnaker已准备可用的jenkins实例 启用 jenkins触发器 官方文档&…

电源控制系统架构(PCSA)之系统控制处理器组件

目录 6.4 系统控制处理器 6.4.1 SCP组件 SCP处理器Core SCP处理器Core选择 SCP处理器核内存 系统计数器和通用计时器 看门狗 电压调节器控制 时钟控制 系统控制 信息接口 电源策略单元 传感器控制 外设访问 系统访问 6.4 系统控制处理器 系统控制处理器(SCP)是…

二 使用GPIO的复用功能 利用USART 实现printf()

参考这篇&#xff1a; STM32串口通信详解 1. 关于USART USART ( universal synchronous / asynchronous receiver /transmitter) 是一种串行通讯协议 , 允许设备通过串行端口进行数据传输&#xff0c; USART 能够以同步或者异步的方式进行工作&#xff0c;在实际的运用中&…

chrome vue devTools安装

安装好后如下图所示&#xff1a; 一&#xff1a;下载vue devTools 下载链接https://download.csdn.net/download/weixin_44659458/13192207?spm1001.2101.3001.6661.1&utm_mediumdistribute.pc_relevant_t0.none-task-download-2%7Edefault%7ECTRLIST%7EPaid-1-13192207…

JavaScript中的for循环你用对了吗?

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;JavaScript篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript-for循环 目录 循环结构 循环思想&#xff08;三要素&#xff09; 实现…

汽车行驶不同工况数据

1、内容简介 略 28-可以交流、咨询、答疑 2、内容说明 汽车行驶不同工况数据 汽车行驶不同工况数据 ECE、EUDC、FTP75、NEDC、自定义 3、仿真分析 4、参考论文 略 链接&#xff1a;https://pan.baidu.com/s/1AAJ_SlHseYpa5HAwMJlk1w 提取码&#xff1a;rvol

[每周一更]-(第75期):Go相关粗浅的防破解方案

Go作为编译语言&#xff0c;天然存在跨平台的属性&#xff0c;我们在编译完成后&#xff0c;可以再不暴露源代码的情况下&#xff0c;运行在对应的平台中&#xff0c;但是 还是架不住有逆向工程师的反编译、反汇编的情形&#xff1b;&#xff08;当然我们写的都不希望被别人偷了…

docker-compose;私有镜像仓库harbor搭建;镜像推送到私有仓库harbor

docker-compose&#xff1b;私有镜像仓库harbor搭建&#xff1b;镜像推送到私有仓库harbor 文章目录 docker-compose&#xff1b;私有镜像仓库harbor搭建&#xff1b;镜像推送到私有仓库harbordocker-compose私有镜像仓库harbor搭建镜像推送到私有仓库harbor docker-compose D…

【代码】基于VMD(变分模态分解)-SSA(麻雀搜索算法优化)-LSTM的光伏功率预测模型(完美复现)matlab代码

程序名称&#xff1a;基于VMD&#xff08;变分模态分解&#xff09;-SSA&#xff08;麻雀搜索算法优化&#xff09;-LSTM的光伏功率预测模型 实现平台&#xff1a;matlab 代码简介&#xff1a;提出了变分模态分解(VMD)和麻雀搜索算法(SSA)与长短期记忆神经网络 (LSTM)相耦合,…

【趣味JavaScript】一文让你读懂JavaScript原型对象与原型链的继承,探秘属性的查找机制! 《重置版》

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

校招笔试-Windows开发工程师客观题合集解析

360公司-2019校招笔试-Windows开发工程师客观题合集 API无法实现进程间数据的相互传递是PostMessage 2.以下代码执行后&#xff0c;it的数据为&#xff08;异常&#xff09; std::list<int> temp; std::list<int>::iterator it temp.begin(); it --it; 3.API…

多线程(初阶五:wait和notify)

目录 一、概念 二、用法 &#xff08;1&#xff09;举个栗子&#xff1a; &#xff08;2&#xff09;wait和notify的使用 1、没有上锁的wait 2、当一个线程被wait&#xff0c;但没有其他线程notify来释放这个wait 3、两个线程&#xff0c;有一个线程wait&#xff0c;有一…