Windows Mobile下WinInet的异步使用方法

codeproject上有一篇老美写的关于HTTP异步的文章:http://www.codeproject.com/KB/IP/asyncwininet.aspx

我做HTTP异步的时候,也是参考了这篇文章,受益匪浅。今天特地翻译出来,与大家共飨。

WinInet HTTP的异步方式使用

绪论

如果你曾经深入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异步方式的人。(之后是老美希望大家购买他的代码,略过...)

发表于 @ 2008年06月06日 16:05:00 | 评论( 9 ) | 编辑| 举报| 收藏

旧一篇:Windows mobile下文件关联程序 | 新一篇:Windows Mobile手机中存储卡的名称识别
查看最新精华文章 请访问博客首页相关文章 Areslee 发表于2008年7月25日 11:23:59  IP:举报回复删除
请教一下,如何修正lvBuffer和dwBufferLength
我在用InternetReadFileEx读的时候,实际上已经读入部分内容了,可是dwBufferLength并没有改变,我完全无法知道到底读了多少pknife 发表于2008年7月26日 20:42:25  IP:举报回复删除
dwBufferLength是你读取部分的长度,你应该把每次读取的内容保存起来。继续读取,如果InternetReadFileEx为真且dwBufferLength为0,则表示数据读取完毕。raylin 发表于2008年7月29日 17:24:25  IP:举报回复删除
请问InternetSetStatusCallback( m_Session,
(INTERNET_STATUS_CALLBACK)InternetCallbackFunc );
中InternetCallbackFunc怎么定义?
我定义成:
VOID CALLBACK CHttpEngine::InternetCallbackFunc(HINTERNET hInternet,DWORD dwContext,DWORD dwInternetStatus,LPVOID lpvStatusInformation,DWORD dwStatusInformationLength)
编译老是出错...


error C2664: 'InternetSetStatusCallback' : cannot convert parameter 2 from 'void (void *,unsigned long,unsigned long,void *,unsigned long)' to 'void (__stdcall *)(void *,unsigned long,unsigned long,voi
d *,unsigned long)'

raylin 发表于2008年7月29日 17:58:44  IP:举报回复删除
提示:
None of the functions with this name in scope match the target typepknife 发表于2008年7月31日 13:58:08  IP:举报回复删除
InternetCallbackFunc函数不要定义为类的成员函数,设成全局的。ligengyu 发表于2009年6月26日 14:47:43  IP:举报回复删除
不知道你有没有具体测试过????那个回调怎么回ylq1116 发表于2009年11月19日 15:39:04  IP:举报回复删除
Mobile 对InternetReadFileEx 不支持。错误代码返回120 ylq1116 发表于2009年11月19日 17:15:55  IP:举报回复删除
搜了半天。得用 InternetReadFileExA() 。。。。wantcomeback 发表于2010年1月19日 16:09:47  IP:举报回复删除
回复 ylq1116:我必须顶你,因为你一句话节约了我至少2个小时时间。就是这个API能异步读取

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/pknife/archive/2008/06/06/2517272.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值