绪论

如果你曾经深入MSDN研究过WinInet API,你会注意到可使用异步方式且该方式是被推崇的。

当你决定使用该方式时,你却找不到如何使用异步的说明。网上也没有任何例子。研究了很长时间,也做了很多试验,我最终决定着手来填补一份(非官方)空缺的文档。

为什么异步方式是最好的?因为它能够正确的处理超时。而在IE5.5下WinInet缺少此功能。

如果你试图使用TerminateThread或CloseHandle函数来处理超时(这些函数在MSDN文档中有介绍),你将落入各种各样的陷阱中。

以下条件中异步测试成功:单处理器和多处理器的WinNT4系统下的IE4.01SP3, IE5.0, IE5.01, IE5.5SP1,压力环境(12小时不间断地在多处理器NT服务器下运行15个并发实例)。

原理
使用WinInet函数的异步方式,你必须按照正确的顺序:
1.使用INTERNET_FLAG_ASYNC打开任务。
2.使用InternetSetStatusCallback设置回调。
3.使用InternetOpenUrl打开连接。
4.如果InternetOpenUrl返回NULL且GetLastError的值是ERROR_IO_PENDING:
  1)等待回调函数返回INTERNET_STATUS_HANDLE_CREATED通知,保存连接句柄;
  2)等待回调函数返回INTERNET_STATUS_REQUEST_COMPLETE通知。
5.解析header里的vontent-length字段,创建一个INTERNET_BUFFERS结构:
  1)dwStructSize = sizeof(INTERNET_BUFFERS);
  2)lpvBuffer = 你申请的缓冲;
  3)dwBufferLength = 缓冲长度。
6.使用InternetReadFileEx函数,参数为IRF_ASYNC,异步读取剩余的数据。不要使用InternetReadFile,因为它是同步的。
7.如果InternetReadFileEx返回FALSE且GetLastError的值为ERROR_IO_PENDING:等待回到函数返回INTERNET_STATUS_REQUEST_COMPLETE 通知。
  警告:INTERNET_BUFFERS结果的成员是会被修改的(dwBufferLength和缓冲区)。
8.如果dwBufferLength不为0,移动lpvBuffer的指针dwBufferLength个长度,重复第6步。
9.使用InternetCloseHandle关闭连接,等待INTERNET_STATUS_HANDLE_CLOSING和特定的INTERNET_STATUS_REQUEST_COMPLETE通知。

之后,你可以开始一个新的连接过程或者关闭任务句柄。但是在关闭之前,你应该卸载回调函数。

细节
在原理中,让我们看看关键点的一些代码:

1&2:使用INTERNET_FLAG_ASYNC打开任务,设置回调
m_Session = InternetOpen(AGENT_NAME, INTERNET_OPEN_TYPE_PRECONFIG, 
                          NULL, NULL, INTERNET_FLAG_ASYNC);
InternetSetStatusCallback( m_Session, 
      (INTERNET_STATUS_CALLBACK)InternetCallbackFunc );

3&4:使用InternetOpenUrl打开连接,等待INTERNET_STATUS_REQUEST_COMPLETE通知
使用lParam发送一个任务表示到你的回调。我总是用this指针来传递我的class。这里假设你知道如何处理回调。
InternetOpenUrl( m_Session, uurl, NULL, 0, 
      INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE | 
      INTERNET_FLAG_NO_CACHE_WRITE, (LPARAM)this );

回调会收到一堆的消息。这是收到的dwInternetStatus值的顺序:
[openUrl] InternetStatus: 60 INTERNET_STATUS_HANDLE_CREATED
**此时你应该保存HINTERNET句柄,如下代码:
INTERNET_ASYNC_RESULT* res = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
m_hHttpFile = (HINTERNET)(res->dwResult);

[openUrl] InternetStatus: 10
[openUrl] InternetStatus: 11
[openUrl] InternetStatus: 20
[openUrl] InternetStatus: 21
[openUrl] InternetStatus: 30
[openUrl] InternetStatus: 31
[openUrl] InternetStatus: 40
[openUrl] InternetStatus: 41
[openUrl] InternetStatus: 100 INTERNET_STATUS_REQUEST_COMPLETE

5:解析content-length,创建INTERNET_BUFFERS结构
一旦你得到了句柄,调用HttpQueryInfo(使用HTTP_QUERY_CONTENT_LENGTH标记)得到接收数据的大小。如果HTTP头里没有content-length参数,函数会失败。
创建INTERNET_BUFFERS结构。
INTERNET_BUFFERS ib = { sizeof(INTERNET_BUFFERS) };
ib.lpvBuffer = 你申请的缓冲
ib.dwBufferLength = 缓冲长度
dwBufferTotal供你自己使用,永远不会被WinInet该变(据我所知)。我用它来存储收到数据的总长度。

6&7&8:循环读取剩余数据
调用InternetReadFileEx(使用IRF_ASYNC标记)异步读取剩余数据。不要使用InternetReadFile,因为它是同步函数。

你必须循环调用InternetReadFileEx,直到ib.dwBufferLength为0。在每次循环前你必须调整lpvBuffer指针位置,重置ib.dwBufferLength。
BOOL bOk = InternetReadFileEx( m_hHttpFile, &ib, IRF_ASYNC, (LPARAM)this );
if(!bOk && GetLastError()==ERROR_IO_PENDING)
  等待...

while( bOk && ib.dwBufferLength!=0 )
{
  (调整ib值)
  bOk = InternetReadFileEx( m_hHttpFile, &ib, IRF_ASYNC, (LPARAM)this );
  if(!bOk && GetLastError()==ERROR_IO_PENDING)
    等待...
}

你的回调函数会收到这些消息:
[connect] InternetStatus: 40 (receiving response)
[connect] InternetStatus: 41 (response received)
[connect] InternetStatus: 50
[connect] InternetStatus: 51
还可能 
[connect] InternetStatus: 100 INTERNET_STATUS_REQUEST_COMPLETE

最后一个只有在GetLastError值为ERROR_IO_PENDING才会得到。如果你用dwBufferTotal存储了数据总大小(按字节),把你的字符缓冲最后一位置置为0(如果是字符的话)。
buf[ib.dwBufferTotal] = 0;

9:关闭连接句柄
InternetCloseHandle( m_httpFile );
关闭时,回调会收到这个消息:
[connect] InternetStatus: 70 INTERNET_STATUS_HANDLE_CLOSING

大多数错误例子中,连接都会出乎意料的关闭。此时,你会收到70在100(INTERNET_STATUS_REQUEST_COMPLETE)之后。

此情形在整个过程都可能会发生。

10:关闭m_Session句柄前
你应该卸载回调:
InternetSetStatusCallback( m_Session, NULL );

WinInet HTTP的异步方式使用相关推荐

  1. asio boost 异步错误处理_boost::ASIO的同步方式和异步方式

    http://blog.csdn.net/zhuky/article/details/5364574 http://blog.csdn.net/zhuky/article/details/536468 ...

  2. android cpu调度策略_「性能优化3.0」Android线程调度异步方式汇总

    线程调度与线程调度模型 任意时刻,只有一个线程占用 CPU,处于运行状态.而多线程并发执行就是轮流获取 CPU 执行权. 分时调用模型 轮流获取 CPU 执行权,均分 CPU 执行时间. 抢占式调度模 ...

  3. 学习笔记 --- 编码过程中常见的三种异步方式

    实际的编码过程中, 凡是涉及到网络通信的代码, 异步都是决不可缺少的. 那么什么是异步呢? 异步就是子线程, 异步通过开辟子线程来实现, 所以一提到异步就应该想到子线程. 即使不涉及网络通信, 异步也 ...

  4. Ajax异步方式实现登录与參数的校验

    Ajax异步方式实现登录与參数的校验 登录代码 这个是使用Bootstrap3的组件功能实现的 <div class="login_con_R"><h4>登 ...

  5. Visual C++串口通信编程---多线程异步方式

    Visual C++串口通信编程---多线程异步方式 1. 串口通信基础 提到串口让人想起并口,它们是计算机中两个比较重要的通信方式. 串口:也叫COM口,把字节的二进制位按位列队进行传输,每个字节占 ...

  6. [转摘]使用异步方式调用同步方法

    使用异步方式调用同步方法 .NET Framework 4.5 .NET Framework 4 Visual Studio 2008 .NET Framework 3.5 .NET Framewor ...

  7. 实验九:采用异步方式实现文件读/写

    一:实验目的 (1)了解Windows系统异步文件读/写的概念. (2)熟悉Windows系统文件读/写相关的API. (3)掌握采用异步方式实现文件读/写的相关参数设置. 二:实验准备知识:文件异步 ...

  8. 实验九 使用异步方式实现文件读\写

    实验九 使用异步方式实现文件读\写 一.实验目的 了解Windows系统异步文件读/写的概念. 熟悉Windows系统文件读/写相关的API. 掌握采用异步方式实现文件读/写的相关参数设置. 二.实验 ...

  9. Cesium 实战 - 最新版(1.104.0)通过异步方式初始化地球,加载影像以及高程图层

    Cesium 实战-最新版(1.104.0)通过异步方式初始化地球,加载影像以及高程图层 遇到问题 初始化底图 初始化高程(监听载入完成事件,开启关闭高程) 初始化 3dtile 在线示例 Cesiu ...

最新文章

  1. StringUtils工具类的isBlank()方法使用说明
  2. PO BO VO DTO POJO DAO概念及其作用(附转换图)
  3. UVALive 7138 The Matrix Revolutions(Matrix-Tree + 高斯消元)(2014 Asia Shanghai Regional Contest)...
  4. return print
  5. windows2003+iis6.0+php(fastcgi)5.3+wincache+memcached
  6. day 68 增删改查 语法
  7. ThinkPHP-保存生成的二维码
  8. spark sql 数据类型转换_spark dataframe 类型转换
  9. 使用Apktools反编译apk应用
  10. docker 镜像备份magento 2.2.3
  11. 瑞信:区块链技术还在半山腰 2025年才能真正成熟
  12. osx php mongodb,Mac OSX 平台安装 MongoDB
  13. 功率谱估值方法matlab仿真——2、经典估值方法介绍
  14. DaZeng:Vue全家桶实现小米商城(二)
  15. CVF 6.6B 安装无反应(响应)及打开无反应(响应)问题(win10系统)
  16. python发邮件图片太长显示不出来_小白入门,用python 发送定时邮件,将Dataframe转为邮件正文,链接显示为图片...
  17. Linux快捷方式There was an error launching the application
  18. python importlib bootstrap_python - importlib._bootstrap和Python解释器初始化 - SO中文参考 - www.soinside.com...
  19. 通过Excel制作下拉框筛选出成绩
  20. Ubuntu开机无法进入桌面

热门文章

  1. Android利剑之——通知栏提醒
  2. 【最终版】PyQt5 自定义标题栏,实现无边框,最小化最大化关闭事件,窗口拖动移动,窗口改变大小,仿百度网盘色调美化,添加内容窗口
  3. oracle转换表结构,SQLServer表结构转换成Oracle表结构
  4. css3的学习笔记1
  5. redis映射的概念_搭建分布式Redis Cluster集群与Redis入门
  6. Spring5框架 笔记总结(二)
  7. 抖音发视频上热门技巧,抖音发什么视频会火
  8. 80款中国风 全动态PPT模板
  9. 题解 P3353 【在你窗外闪耀的星星】
  10. USBCNC 雕刻双面PCB教程