InternetOpenUrl仅在下载整个HTTP响应后返回

bri*_*anb 5 c++ winapi http wininet

我正在使用WinINET编写一个下载文件实用程序,并注意到(特别是在大型下载时),WinINET InternetOpenUrl()调用仅在下载完整个HTTP响应后才返回.

我通过使用Charles代理工具以及使用WireShark来确认这一点,并注意到下载完全完成,然后WinINET通知我的代码.

一些简化(同步)​​代码:

hInt = InternetOpen(USER_AGENT_NAME, INTERNET_OPEN_TYPE_PRECONFIG, 
                    NULL, NULL, 0);
DWORD dwRequestFlags = INTERNET_FLAG_NO_UI   // no UI please
            |INTERNET_FLAG_NO_AUTH           // don't authenticate
            |INTERNET_FLAG_PRAGMA_NOCACHE    // do not try the cache or proxy
            |INTERNET_FLAG_NO_CACHE_WRITE;   // don't add this to the IE cache

hUrl = InternetOpenUrl(hInt, szURL, NULL, 0, dwRequestFlags, NULL);
if (hUrl)
{
  // <only gets here after entire download is complete>

  InternetCloseHandle(hUrl);
}
InternetCloseHandle(hInt);
Run Code Online (Sandbox Code Playgroud)

文档建议这发送请求,并处理响应的标头(未完成下载),然后您应该运行InternetReadFile()循环直到它返回TRUE并且dwNumberOfBytesRead为0.

来自MSDN
InternetOpenUrl功能: InternetOpenUrl函数解析URL字符串,建立与服务器的连接,并准备下载URL标识的数据.然后,应用程序可以使用InternetReadFile [...]来检索URL数据.

InternetReadFile功能: 为确保检索所有数据,应用程序必须继续调用InternetReadFile函数,直到函数返回TRUE且lpdwNumberOfBytesRead参数等于零.

我也尝试使用异步方法,并注意到同样的事情.具体来说,INTERNET_STATUS_RESPONSE_RECEIVED只有在下载完成后才会发送到已注册的回调方法.这意味着我的客户端只能在下载完成后才能开始访问数据.

与此类似,我实现了一个使用WinHttp库的版本,并注意到完全相同的结果.

在超时方面,这会让事情变得棘手.如果下载超过超时(默认值为30秒),则InternetOpenUrl()失败.

所以我有两个问题:

如果这是WinInet和WinHttp库的预期行为,为什么文档建议循环InternetReadFile()调用,为什么不只是读取整个缓冲区(在所有WinINET已经存在之后)?

我知道提供这种功能,因为你并不总是想分配150MB的内存块,但提供的借口是你不知道有多少数据可用......但是WinINET已经完成了下载.

recv()如果它只是对临时文件或IE缓存中的文件(或更糟糕的,浪费的内存块)的抽象,它为什么看起来非常像包装的方法呢?

我应该将超时长度设置为什么?如果我不知道数据在超时之前有多大,那么我该如何决定将超时值设置为什么?

这是预期的行为吗?如果是这样的话,有什么方法可以在流式传输时获取数据?

在慢速连接或大文件上,可以想象在整个下载完成之前可以对数据进行大量工作.在经典的Berkley套接字重新实现HTTP的过程中,循环recv()调用将为我提供数据,因为它是最终需要的.

是的我可以使用简单的套接字重写一个实现,但我宁愿不必浪费时间来支持整个HTTP规范和SSL加密,更不用说WinINET中的代理支持了.

bri*_*anb 12

我知道回答你自己的问题可能不礼貌,但我相信我找出了问题所在.

重新启动后(自动更新上浪费了很多很多很多分钟)我再次尝试,并遇到了同样的问题,但我从Alex K.和JJ的评论表明这不是预期的行为,并开始调查运行的软件在可能会干扰的机器上.

在许多应用程序被终止,许多服务被关闭之后,我偶然发现了一个我真的希望不会产生这种效果的服务,但它确实如此.

我关闭了"卡巴斯基实验室网络代理",嘿嘿,InternetOpenUrl在下载HTTP响应开始后大约2秒后返回.我会立即优先考虑,但75秒下载中的第二或第二次至少会给WinINET时间来处理标题并执行它可能需要的任何预处理.

事实证明,如果我不从InternetReadFile()读取数据,下载永远不会完成(通过Charles看到),暗示(希望)InternetReadFile()确实是recv()调用的包装器(就像我一样)本来应该的).

连续重新启用和禁用网络代理服务验证了此发现.我想以某种方式最终证明(或反驳)这一点.

事实证明,我的(读取:IT安全部门)反病毒的选择及其拦截 - 所有网络层通信保护似乎是导致问题的原因.

  • 布莱恩,干得好.这是一个很好的案例研究.我们的软件环境在现实世界中的复杂性的一个很好的例子. (3认同)