异步使用 InternetReadFile() 的正确方法

Posted

技术标签:

【中文标题】异步使用 InternetReadFile() 的正确方法【英文标题】:Correct way to use InternetReadFile() asynchronously 【发布时间】:2013-04-01 17:18:24 【问题描述】:

我有使用 WinInet API 异步执行 HTTP 请求的代码。一般来说,我的代码有效,但我对“正确”的做事方式感到困惑。在InternetReadFile() 的文档中,它指出:

为确保检索到所有数据,应用程序必须继续调用 InternetReadFile 函数,直到函数返回 TRUE 并且 lpdwNumberOfBytesRead 参数为零。

但在异步模式下,它可能(也可能不会)返回 false,以及 ERROR_IO_PENDING 的错误,表明它将异步完成工作,并在完成时调用我的回调。如果我从字面上阅读文档,似乎异步调用也可以只对请求的缓冲区进行部分读取,并要求调用者继续调用 InternetReadFile 直到遇到 0 字节的读取。

同步使用InternetReadFile() 的典型实现如下所示:

while(InternetReadFile(Request, Buffer, BufferSize, &BytesRead) && BytesRead != 0)

    // do something with Buffer

但是任何对InternetReadFile() 的调用都可能表明它将异步完成工作(可能读取部分,但不是您的全部请求),它变得更加复杂。如果我向MSDN sample code 寻求指导,那么实现很简单,只需调用InternetReadFile() 一次,并期望一次返回立即或异步读取整个请求的缓冲区。这是使用此函数的正确方法,还是 MSDN 示例代码忽略了InternetReadFile() 只会读取请求缓冲区的一部分的可能性?

【问题讨论】:

【参考方案1】:

在更仔细地阅读异步示例之后,我现在看到它正在重复读取,直到遇到成功读取 0 字节。因此,要回答我自己的问题,您必须一遍又一遍地调用 InternetReadFile(),并为同步或异步响应做好准备。

【讨论】:

【参考方案2】:

重复读取InternetReadFile() 直到它返回TRUE 并且BytesRead 为0 是使用InternetReadFile() 的正确方法,但如果异步工作还不够。

正如 MSDN 所说

当异步运行时,如果对 InternetReadFile 的调用未导致事务完成,它将返回 FALSE,随后对 GetLastError 的调用将返回 ERROR_IO_PENDING。事务完成后,在先前调用 InternetSetStatusCallback 中指定的 InternetStatusCallback 将使用 INTERNET_STATUS_REQUEST_COMPLETE 调用。

因此,如果您在异步模式下工作,InternetReadFile() 可能会返回 FALSE 并将最后一个错误设置为 ERROR_IO_PENDING 值。

INTERNET_STATUS_REQUEST_COMPLETE 再次调用InternetSetStatusCallback 时,lpvStatusInformation 参数将包含INTERNET_ASYNC_RESULT 结构的地址(请参阅InternetStatusCallback callback function)。 INTERNET_ASYNC_RESULT.dwResult 成员将包含异步操作的结果(TRUEFALSE,因为您调用了 InternetReadFile)并且 INTERNET_ASYNC_RESULT.dwError 将包含错误代码,仅当 dwResultFALSE 时。

如果dwResultTRUE,那么您的Buffer 包含从Internet 读取的数据,而BytesRead 包含异步读取的字节数。

所以当你异步工作时最重要的事情之一是BufferBytesRead 必须在InternetStatusCallback 调用之间保持不变,即不能在堆栈上分配。否则它有未定义的行为,导致内存损坏等。

【讨论】:

以上是关于异步使用 InternetReadFile() 的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

InternetReadFile 因分块响应而失败

InternetReadFile() 似乎没有读取互联网数据

InternetReadFile 问题(错误 87 - 参数不正确)

InternetReadFile 返回损坏的响应

如何使用 InternetOpen、InternetOpenUrl 和 InternetReadFile [关闭]

使用 WinAPI 的 InternetReadFile() 进行长轮询