65.9K
CodeProject 正在变化。 阅读更多。
Home

C++ 中的异步 HTTP 请求 WinINet 包装器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (43投票s)

2009年11月17日

CPOL

2分钟阅读

viewsIcon

140073

downloadIcon

8807

C++ 的异步 HTTP 下载类

引言

WinINet 使网络编程更加容易,但由于其 C 风格的接口,在 C++ 中使用起来比较困难。因此,我编写了这个代码来对其进行封装。使用它,您可以轻松创建异步 HTTP 请求并接收事件回调。它可以应用于 MFC 和 ATL 项目。

这段代码主要由两个类组成:类 FCHttpRequest 和类 FCHttpRequestManager

FCHttpRequest 负责实现发送 HTTP 请求和接收响应,它是 WinINet 的一个封装类。

FCHttpRequestManagerFCHttpRequest 的管理器,负责添加、删除 FCHttpRequest 对象并接收来自 FCHttpRequest 的事件。

与 STL 和 Boost 类似,所有源代码都由 .h.inl 文件组成,您可以轻松地将其集成到您的程序中。

如何使用

  1. 在您的项目中包含 http_request_manager.h 文件。通常在 stdafx.h 的末尾包含它。
  2. 创建从类 FCHttpRequestManager 派生的 HTTP 请求管理器类。
    class CMyManager : public FCHttpRequestManager
    {
        //...
    };
    
    // multiple inheritance attach an exist class
    class CMyManager : public CDialog,
                       public FCHttpRequestManager
    {
        virtual void OnAfterRequestSend (FCHttpRequest& rTask)
        {
            // ...
        }
    
        virtual void OnAfterRequestFinish (FCHttpRequest& rTask)
        {
            // ...
        }
    };

    管理器将接收两个事件:OnAfterRequestSendOnAfterRequestFinish,覆盖您感兴趣的事件。大多数情况下,我们应该覆盖 OnAfterRequestFinish 来处理接收到的数据。

  3. 现在,您可以通过调用 AddRequest 发送 HTTP 请求。

示例

... 下载文件

发送 HTTP 请求

// download whole file
AddDownload (_T("http://phoxo.com/t.zip")) ;

// download file and specifies the starting position
HTTP_REQUEST_HEADER   h ;
h.m_url = _T("http://phoxo.com/t.zip") ;
h.m_start = 30 ; // in byte
AddRequest (h) ;

响应完成事件,处理下载的数据

void OnAfterRequestFinish (FCHttpRequest& rTask)
{
    const HTTP_RESPONSE_INFO   & r = rTask.GetResponseInfo() ;

    bool   bOK = false ;
    if (r.m_status_code == HTTP_STATUS_OK)
    {
        if (r.m_content_length)
        {
            // avoid network broken during downloading
            if (r.m_content_length == rTask.GetTotalReceiveByte())
                bOK = true ;
        }
        else
        {
            // no Content-Type field, maybe is a dynamic page, such as PHP, ASP...
            if (r.m_final_read_result)
                bOK = true ;
        }
    }

    if (bOK)
    {
        std::string   receive_data ;
        rTask.PopReceived (receive_data) ;

        // ... process received data
    }
}

... 指定 User-Agent

// default User-Agent is same to User-Agent of IE
HTTP_REQUEST_HEADER   h ;
h.m_url = _T("http://phoxo.com/") ;
h.m_user_agent = _T("My User-Agent") ;
AddRequest (h) ;

... 自定义 HTTP Header

HTTP_REQUEST_HEADER   h ;
h.m_url = _T("http://phoxo.com/") ;

// \r\n at end of each line
h.m_header += _T("Referer: http://google.com\r\n") ;
h.m_header += _T("Accept: */*\r\n") ;
h.m_header += _T("x-flash-version: 10,0,0,1\r\n") ;

AddRequest (h) ;

... 指定 Proxy

HTTP_REQUEST_HEADER   h ;
h.m_url = _T("http://phoxo.com/") ;
h.m_proxy_ip = _T("8.8.8.8") ;
h.m_proxy_port = 8080 ;
AddRequest (h) ;

... 解压缩 GZip HTTP 数据

此功能需要 ZLib 库,您可以从 http://www.zlib.org 获取它。
按以下顺序包含头文件

#include "zlib.h"
#include "http_request_manager.h"
#include "utility/gzip_decompress.h"

在请求头中添加 Accept-Encoding 字段,通知服务器您可以接受 gzip 压缩的数据

HTTP_REQUEST_HEADER   h ;
h.m_url = _T("http://phoxo.com/") ;
h.m_header += L"Accept-Encoding: gzip, deflate\r\n" ;
AddRequest (h) ;

在完成回调中,您可以使用 gzip_decompress 来解压缩接收到的数据。

void OnAfterRequestFinish (FCHttpRequest& rTask)
{
    std::string   receive_data ;
    rTask.PopReceived (receive_data) ;

    if (IsReceiveGZipStream(rTask) && receive_data.size())
    {
        std::string   raw_data ;
        if (gzip_decompress (receive_data.c_str(), receive_data.size(), raw_data))
        {
            receive_data = raw_data ;
        }
    }
}

... Post multipart/form-data

// this example loads image to memory, then posts to server using multipart/form-data
std::vector   buf ;
FCFileEx::Read (_T("c:\\1.jpg"), buf) ;

HTTP_REQUEST_HEADER   h (HTTP_REQUEST_HEADER::VERB_TYPE_POST_MULTIPART) ;
h.m_url = _T("http://phoxo.com/") ;
h.AddMultipartFormData ("param1", "value1") ;
h.AddMultipartFormData ("param2", "value2") ;
h.AddMultipartFormData ("pic", &buf[0], buf.size(), "1.jpg") ;
h.EndMultipartFormData() ;
m_task_id = AddRequest (h) ;

... HTTPS

HTTP_REQUEST_HEADER   h ;
h.m_url = _T("https://phoxo.com/") ;

// ignores errors that can be caused by the certificate 
// host name of the server not matching the host name in the request
h.m_open_flag |= INTERNET_FLAG_IGNORE_CERT_CN_INVALID ;

// ignores errors that can be caused by an expired server certificate
h.m_open_flag |= INTERNET_FLAG_IGNORE_CERT_DATE_INVALID ;

AddRequest (h) ;

历史

  • 2011 年 8 月 25 日:V2.0
    • 重构代码,更多功能,更高效
    • 添加了 multipart/form-data POST 支持
    • 添加了 HTTPS 支持
    • 添加了 GZip 解压缩支持
  • 2010 年 1 月 30 日:V1.1
    • 重命名为 FCHttpDownload ,从 FCHttpDownloadExec
    • 修复了在回调中弹出模态对话框时的一个错误
    • 修改了 FCHttpRequestManager 的接口
    • 从 Internet Explorer 获取默认 User-Agent
  • 2009 年 11 月 17 日:初始发布
© . All rights reserved.