zoukankan      html  css  js  c++  java
  • Windows Mobile下WinInet的异步使用方法

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

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

    WinInet HTTP的异步方式使用

    绪论

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

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

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

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

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

    原理
    使用WinInet函数的异步方式,你必须按照正确的顺序:
    1.使用INTERNET_FLAG_ASYNC打开任务。
    2.使用InternetSetStatusCallback设置回调。
    3.使用InternetOpenUrl打开连接。
    4.如果InternetOpenUrl返回NULLGetLastError的值是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返回FALSEGetLastError的值为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_CO
    MPLETE通知。

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

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

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

  • 相关阅读:
    memcache的最佳实践方案。
    ehcache memcache redis 三大缓存男高音
    微服务-----(网站架构演变)
    分布式job-任务调度(一)
    rocketmq(三 java操作rocket API, rocketmq 幂等性)
    rocketMQ(二 )Centos7 集群
    go基础三 数组,切片,map,list
    go语言基础二 函数 ,递归函数 ,指针
    go语言基础一:数据类型,变量和常量,类型转化
    Spring AOP 学习(一) 代理模式
  • 原文地址:https://www.cnblogs.com/fengju/p/6173561.html
Copyright © 2011-2022 走看看