C/C++ 通过HTTP实现文件上传下载
WinInet(Windows Internet)是 Microsoft Windows 操作系统中的一个 API 集,用于提供对 Internet 相关功能的支持。它包括了一系列的函数,使得 Windows 应用程序能够进行网络通信、处理 HTTP 请求、FTP 操作等。WinInet 提供了一套完整的网络通信工具,使得开发者能够轻松地构建支持网络功能的应用程序,涵盖了从简单的 HTTP 请求到复杂的文件传输等多种网络操作。
分解URL地址
InternetCrackUrl 函数可实现对URL字符串进行解析,提取其中的协议、主机名、端口、路径和其他信息,并将这些信息存储在 URL_COMPONENTS 结构中,方便后续的网络操作,该函数是Windows下默认提供的,函数与依赖结果如下所示;
函数原型
BOOL InternetCrackUrl( |
参数说明
lpszUrl:指定待解析的 URL 字符串。dwUrlLength:指定 URL 字符串的长度。dwFlags:指定解析 URL 的标志,可以是以下值之一:ICU_DECODE:对 URL 进行解码。ICU_ESCAPE:对 URL 进行转义。
lpUrlComponents:一个指向URL_COMPONENTS结构的指针,用于存储解析后的各个部分信息。
URL_COMPONENTS结构
typedef struct { |
返回值
如果函数成功,返回 TRUE,并在 lpUrlComponents 结构中存储解析后的信息;如果失败,返回 FALSE。在失败时,可以调用 GetLastError 函数获取详细的错误信息。
函数调用
|
运行代码输出特定网址的每个部分,如下图所示;

下载页面内容
InternetOpen
用于初始化 WinINet 函数的使用。以下是该函数的原型:
HINTERNET InternetOpen( |
参数说明:
lpszAgent: 指定应用程序的名称,用于标识调用InternetOpen的应用程序。dwAccessType: 指定访问类型,可以是INTERNET_OPEN_TYPE_DIRECT、INTERNET_OPEN_TYPE_PROXY或INTERNET_OPEN_TYPE_PRECONFIG中的一个。lpszProxyName: 如果dwAccessType是INTERNET_OPEN_TYPE_PROXY,则指定代理服务器的名称。否则,可以设为NULL。lpszProxyBypass: 如果dwAccessType是INTERNET_OPEN_TYPE_PROXY,则指定绕过代理服务器的地址。否则,可以设为NULL。dwFlags: 一些标志,可以用来指定额外的行为,如INTERNET_FLAG_ASYNC用于异步操作。
返回值:
如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,用于后续的 WinINet 操作。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。
InternetConnect
用于建立到远程服务器的连接。以下是该函数的原型:
HINTERNET InternetConnect( |
参数说明:
hInternet: 调用InternetOpen返回的句柄,表示连接的上下文。lpszServerName: 要连接的服务器的名称或 IP 地址。nServerPort: 服务器的端口号。lpszUserName: 连接服务器时要使用的用户名,可以为NULL。lpszPassword: 连接服务器时要使用的密码,可以为NULL。dwService: 指定服务类型,可以是INTERNET_SERVICE_FTP、INTERNET_SERVICE_HTTP或其他服务类型。dwFlags: 一些标志,用于指定连接的属性,如INTERNET_FLAG_RELOAD、INTERNET_FLAG_SECURE等。dwContext: 用户定义的应用程序上下文,将在回调函数中使用。
返回值:
如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,表示连接的上下文。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。
InternetConnect 用于建立连接后,可以使用返回的句柄执行相关的协议操作,如 FTP 或 HTTP 操作。使用完连接后,同样需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。
HttpOpenRequest
它是在使用 WinINet 库进行 HTTP 操作时的一部分。以下是该函数的原型:
HINTERNET HttpOpenRequest( |
参数说明:
hConnect: 调用InternetConnect返回的连接句柄,表示请求的上下文。lpszVerb: HTTP 请求方法,如 “GET”、”POST” 等。lpszObjectName: 请求的对象名,通常是 URL 的路径部分。lpszVersion: HTTP 协议版本,通常是 “HTTP/1.1”。lpszReferrer: 引用的来源,可以为NULL。lplpszAcceptTypes: 指定可接受的媒体类型,可以为NULL。dwFlags: 一些标志,用于指定请求的属性,如INTERNET_FLAG_RELOAD、INTERNET_FLAG_SECURE等。dwContext: 用户定义的应用程序上下文,将在回调函数中使用。
返回值:
如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,表示打开的 HTTP 请求。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。
一旦打开了 HTTP 请求,可以使用返回的句柄执行发送请求、接收响应等操作。使用完请求后,同样需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。
HttpSendRequest
用于发送 HTTP 请求的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:
BOOL HttpSendRequest( |
参数说明:
hRequest: 调用HttpOpenRequest返回的 HTTP 请求句柄,表示要发送请求的上下文。lpszHeaders: 包含请求头信息的字符串,可以为NULL。dwHeadersLength: 请求头的长度,如果lpszHeaders是NULL,则可以为零。lpOptional: 包含请求的可选数据的缓冲区,可以为NULL。dwOptionalLength: 可选数据的长度,如果lpOptional是NULL,则可以为零。
返回值:
如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。
HttpSendRequest 用于实际发送 HTTP 请求。在调用此函数之后,可以使用其他 WinINet 函数来读取服务器的响应。同样,使用完请求后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。
HttpQueryInfo
用于检索有关 HTTP 请求或响应的信息的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:
BOOL HttpQueryInfo( |
参数说明:
hRequest: 调用HttpOpenRequest返回的 HTTP 请求句柄,表示要查询信息的上下文。dwInfoLevel: 指定要检索的信息类型,可以是预定义的常量,如HTTP_QUERY_STATUS_CODE、HTTP_QUERY_CONTENT_TYPE等。lpBuffer: 用于接收检索到的信息的缓冲区。lpdwBufferLength: 指向一个变量,表示lpBuffer缓冲区的大小。在调用函数前,应该将该变量设置为lpBuffer缓冲区的大小。在调用函数后,该变量将包含实际写入缓冲区的字节数。lpdwIndex: 如果请求返回多个值,可以使用此参数指定要检索的值的索引。对于单值的信息,可以将其设置为NULL。
返回值:
如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。
HttpQueryInfo 用于获取与 HTTP 请求或响应相关的信息,如状态码、内容类型等。注意,在调用此函数之前,通常需要先调用 HttpSendRequest 发送请求。同样,使用完请求后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。
InternetReadFile
用于从指定的句柄读取数据的函数,通常在使用 WinINet 库进行网络操作时调用。以下是该函数的原型:
BOOL InternetReadFile( |
参数说明:
hFile: 调用HttpOpenRequest或FtpOpenFile返回的句柄,表示要读取数据的上下文。lpBuffer: 用于接收读取到的数据的缓冲区。dwNumberOfBytesToRead: 指定要读取的字节数。lpdwNumberOfBytesRead: 指向一个变量,表示lpBuffer缓冲区中实际读取的字节数。在调用函数前,应该将该变量设置为lpBuffer缓冲区的大小。在调用函数后,该变量将包含实际读取的字节数。
返回值:
如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。
InternetReadFile 用于从网络资源中读取数据,如从 HTTP 请求的响应中读取内容。在调用此函数之前,通常需要先调用其他相关的函数,如 HttpOpenRequest、HttpSendRequest 和 HttpQueryInfo。同样,使用完资源后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。
下载页面的完整代码是这样的,如下所示;
|
使用时调用HttpDownload实现数据下载,下载后的文件会保存在pHttpDownloadData中,此时直接调用SaveToFile将其保存在文件中即可;
int main(int argc, char* argv[]) |
运行后则可一输出响应头Content-Length:完整参数以及输出的字节数,如下图所示;

上传文件内容
服务端,首先需要实现一个简单的上传接收功能,这里使用flask框架实现,通过执行pip install flask命令安装这个库,安装成功以后手动保存为main.py文件,上传文件是只需要向http://127.0.0.1/upload?file=推送数据即可,代码如下;
from flask import Flask, request |
服务端以管理员身份运行main.py文件,此时会启用一个Web服务器用于接收客户端的上传请求,如下图所示;

接着是客户端的实现,首先介绍如下几个关键API函数;
HttpSendRequestEx
用于发送带有附加选项的 HTTP 请求。相对于 HttpSendRequest,它提供了更多的灵活性,允许在请求中包含额外的信息,例如头部和数据。
以下是 HttpSendRequestEx 的原型:
BOOL HttpSendRequestEx( |
参数说明:
hRequest:由HttpOpenRequest返回的句柄,表示 HTTP 请求。lpBuffersIn:指向INTERNET_BUFFERS结构的指针,其中包含要作为请求的一部分发送的数据。lpBuffersOut:指向INTERNET_BUFFERS结构的指针,用于接收响应中接收到的数据。dwFlags:附加标志,控制函数的行为。这可以包括选项,如INTERNET_FLAG_RELOAD、INTERNET_FLAG_SECURE等。dwContext:传递给回调函数的用户定义的上下文值。
INTERNET_BUFFERS 是一个结构,允许您在 HTTP 请求和响应中指定用于发送和接收数据的缓冲区。
使用 HttpSendRequestEx 需要谨慎处理内存,并根据您的需求设置 INTERNET_BUFFERS 结构的具体方式。
InternetWriteFile
用于将数据写入到由 InternetOpenUrl、InternetOpen、HttpOpenRequest 或 FtpOpenFile 等函数打开的 URL、连接或文件。以下是该函数的原型:
BOOL InternetWriteFile( |
参数说明:
hFile: 调用InternetOpenUrl、InternetOpen、HttpOpenRequest或FtpOpenFile等函数返回的句柄,表示要写入的文件、URL 或连接。lpBuffer: 指向包含要写入的数据的缓冲区的指针。dwNumberOfBytesToWrite: 要写入的字节数。lpdwNumberOfBytesWritten: 指向一个变量,表示实际写入的字节数。在调用函数前,应该将该变量设置为缓冲区的大小。在调用函数后,该变量将包含实际写入的字节数。
返回值:
如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。
InternetWriteFile 主要用于将数据写入网络资源,如通过 HTTP 或 FTP 协议上传文件。在调用此函数之前,通常需要先调用其他相关的函数,如 InternetOpenUrl、InternetOpen、HttpOpenRequest 等。同样,使用完资源后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。
HttpEndRequest
它通常与 HttpSendRequest 或 HttpSendRequestEx 配合使用,用于完成 HTTP 请求的发送,并准备接收服务器的响应。
以下是 HttpEndRequest 函数的原型:
BOOL HttpEndRequest( |
参数说明:
hRequest: 调用HttpOpenRequest、HttpOpenRequestEx、HttpSendRequest或HttpSendRequestEx等函数返回的 HTTP 请求句柄。lpBuffersOut: 指向一个INTERNET_BUFFERS结构的指针,该结构用于传递关于响应数据的信息。可以为NULL。dwFlags: 一些标志,用于指定结束请求的选项。通常为 0。dwContext: 用户定义的应用程序上下文,将在回调函数中使用。
返回值:
如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。
HttpEndRequest 的主要作用是完成 HTTP 请求的发送,并在请求完成后准备接收服务器的响应。在调用此函数之后,通常会使用 InternetReadFile 函数等来读取服务器的响应数据。
上传文件的完整代码是这样的,如下所示;
|
上传代码通过指定szHttpUploadUrl将d://lyshark.exe文件提交到远程主机,代码如下所示;
int main(int argc, char* argv[]) |
运行后提交文件,则可以看到输出信息,如下图所示;
