欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

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

最编程 2024-03-10 20:26:50
...

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] = {
   
    
						

上一篇: http 下载原理

下一篇: http 协议大文件下载