RT
用wininet,使用异步模式进行文件的http上传和下载时,读写api函数返回并不代表发送的数据真的已经被网卡发送给server且收到server的ack了。
举例来说
put方式进行文件上传时,InternetWriteFile函数可以很快的提交要发送的数据,函数原型如下
BOOLAPI InternetWriteFile(
    __in HINTERNET hFile,
    __in_bcount(dwNumberOfBytesToWrite) LPCVOID lpBuffer,
    __in DWORD dwNumberOfBytesToWrite,
    __out LPDWORD lpdwNumberOfBytesWritten
    );
根据lpdwNumberOfBytesWritten参数返回来的字节数来做进度控制是不准确的,因为虽然InternetWriteFile成功写入,但实际网络并未完成数据的发送。这样导致在UI交互层面,会发现进度条很快走到了99%、100%,然后很长时间卡在那里不动,等待http的complete回调(也就是server返回最终的200)。这个问题在网速越慢的机器上越发的明显,例如发送一个10MB的文件,InternetWriteFile可以很快的把这10M的数据write给网卡,然后进度条就到了100%,但实际数据发送却是很长时间,进度也就卡了很长时间,才显示上传成功。为了尝试解决这个问题,首先认为是cache的问题,在HttpOpenRequest的时候,指定flag中含有INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_PRAGMA_NOCACHE,但发现效果一致,没有改善。其次,查看msdn,wininet的http异步方式主要靠事件回调来通知各种状态和进展。如INTERNET_STATUS_REQUEST_COMPLETE表示请求完成收到server响应,其中有两个回调id:INTERNET_STATUS_REQUEST_SENT和INTERNET_STATUS_RESPONSE_RECEIVED,msdn中的解释分别是Successfully sent the information request to the server. 和Successfully received a response from the server.回调参数中有发送和接收的字节数。但实际测试,这两个回调也是不靠谱的,并不是真正收到server的ack说多少多少字节我收到了。请求各位大侠帮忙~~跪谢~~~
wininethttp上传下载进度控制InternetWriteFileSTATUS_REQUEST_SENT

解决方案 »

  1.   

    不然就是用socket来做,这样可以很好的控制进度等
      

  2.   


    http的方式应该是能做到的。socket的方式肯定最直接和准确,只不过整个程序也不是专业的下载软件,已经封装了一套http库,暂时没打算换做socket方式去实现。求教wininet是否支持flush操作呢?
      

  3.   

    BOOL CHttpUploadFileProc::UseHttpSendReqEx(HINTERNET hRequest, CFile &file)
    {
    // 生成form-data协议信息 <begin>
    CStringA straHeader;
    CStringA straContentHead;
    CStringA straContentTail; straHeader = GetHttpAppendHeader();
    straContentHead = GetContentHead(file.GetFileName());
    straContentTail = GetContentTail();
    // 生成form-data协议信息 <end> ULONGLONG ullFileLength = file.GetLength();
    DWORD dwContentLength = straContentHead.GetLength() + (DWORD) ullFileLength + straContentTail.GetLength(); INTERNET_BUFFERS BufferIn = {0};
    BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will occur
    BufferIn.Next = NULL; 
    //BufferIn.lpcszHeader = NULL;
    //BufferIn.dwHeadersLength = 0;
    BufferIn.lpcszHeader = straHeader.LockBuffer();
    straHeader.UnlockBuffer();
    BufferIn.dwHeadersLength = (DWORD)straHeader.GetLength();
    BufferIn.dwHeadersTotal = 0;
    BufferIn.lpvBuffer = NULL;                
    BufferIn.dwBufferLength = 0;
    BufferIn.dwBufferTotal = dwContentLength;
    BufferIn.dwOffsetLow = 0;
    BufferIn.dwOffsetHigh = 0; if (IsTerminated()) return FALSE;
    NotifyReceiver(P_HUF_SENDING_FILE, 0);
    if(!HttpSendRequestEx( hRequest, &BufferIn, NULL, 0, 0))
    return FALSE; UINT uProgressBegin = 10;
    UINT uProgressEnd = 90;
    UINT uProgressCur = 0;
    UINT uReadCountSum = 0;
    const UINT uBufSize = 1024;
    BYTE pBuffer[uBufSize];
    UINT uReadCount;
    DWORD dwBytesWritten;
    BOOL bRet;
    float fSendPercent;
    UINT uProgressScale;
    // Write Head
    bRet = InternetWriteFile( hRequest, straContentHead.LockBuffer(), straContentHead.GetLength(), &dwBytesWritten);
    straContentHead.UnlockBuffer();
    if(!bRet)
    {
    NotifyReceiver(E_HUF_SEND_FILE_FAILED, 0);
    return FALSE;
    } if (IsTerminated()) return FALSE;
    // Write file contents
    uReadCountSum = 0;
    bRet = TRUE;
    file.SeekToBegin();
    do{
    if (IsTerminated()) return FALSE;
    uReadCount = file.Read(pBuffer, uBufSize);
    if (0 == uReadCount)
    break; if(! InternetWriteFile( hRequest, pBuffer, uReadCount, &dwBytesWritten))
    {
    NotifyReceiver(E_HUF_SEND_FILE_FAILED, 0);
    return FALSE;
    } uReadCountSum += uReadCount;
    fSendPercent = (float)uReadCountSum / BufferIn.dwBufferTotal;
    uProgressScale = (UINT) ((uProgressEnd - uProgressBegin) * fSendPercent);
    uProgressCur = uProgressBegin + uProgressScale;
    NotifyReceiver(P_HUF_PROGRESS, uProgressCur); } while (uReadCount == uBufSize); if (IsTerminated()) return FALSE;
    // Write Tail
    bRet = InternetWriteFile( hRequest, straContentTail.LockBuffer(), straContentTail.GetLength(), &dwBytesWritten);
    straContentTail.UnlockBuffer();
    if(!bRet)
    {
    NotifyReceiver(E_HUF_SEND_FILE_FAILED, 0);
    return FALSE;
    }
    NotifyReceiver(P_HUF_PROGRESS, uProgressEnd);
    if (IsTerminated()) return FALSE;
    NotifyReceiver(P_HUF_WAIT_RESPONSE, 0);
    if (!HttpEndRequest(hRequest, NULL, 0, 0))
    {
    NotifyReceiver(E_HUF_WAIT_RESPONSE_FAILD, 0);
    bRet = FALSE;
    } return bRet;
    }
      

  4.   


    谢谢这位大大,有两个疑问。
    发送文件内容的逻辑中
    if(! InternetWriteFile( hRequest, pBuffer, uReadCount, &dwBytesWritten))
            {
                NotifyReceiver(E_HUF_SEND_FILE_FAILED, 0);
                return FALSE;
            }
     
            uReadCountSum += uReadCount;
            fSendPercent = (float)uReadCountSum / BufferIn.dwBufferTotal;
            uProgressScale = (UINT) ((uProgressEnd - uProgressBegin) * fSendPercent);
            uProgressCur = uProgressBegin + uProgressScale;
            NotifyReceiver(P_HUF_PROGRESS, uProgressCur);进度是用uReadCount累加的,但实际应该用dwBytesWritten。另外,整个上传逻辑中的上传进度回调跟我描述的应该是一致的,都是在InternetWriteFile返回后就上报进度,同样存在不准确的问题吧。
      

  5.   


    谢谢这位大大,有两个疑问。
    发送文件内容的逻辑中
    if(! InternetWriteFile( hRequest, pBuffer, uReadCount, &dwBytesWritten))
            {
                NotifyReceiver(E_HUF_SEND_FILE_FAILED, 0);
                return FALSE;
            }
     
            uReadCountSum += uReadCount;
            fSendPercent = (float)uReadCountSum / BufferIn.dwBufferTotal;
            uProgressScale = (UINT) ((uProgressEnd - uProgressBegin) * fSendPercent);
            uProgressCur = uProgressBegin + uProgressScale;
            NotifyReceiver(P_HUF_PROGRESS, uProgressCur);进度是用uReadCount累加的,但实际应该用dwBytesWritten。另外,整个上传逻辑中的上传进度回调跟我描述的应该是一致的,都是在InternetWriteFile返回后就上报进度,同样存在不准确的问题吧。没有不准确,因为每次读写都是1024字节
      

  6.   


    谢谢这位大大,有两个疑问。
    发送文件内容的逻辑中
    if(! InternetWriteFile( hRequest, pBuffer, uReadCount, &dwBytesWritten))
            {
                NotifyReceiver(E_HUF_SEND_FILE_FAILED, 0);
                return FALSE;
            }
     
            uReadCountSum += uReadCount;
            fSendPercent = (float)uReadCountSum / BufferIn.dwBufferTotal;
            uProgressScale = (UINT) ((uProgressEnd - uProgressBegin) * fSendPercent);
            uProgressCur = uProgressBegin + uProgressScale;
            NotifyReceiver(P_HUF_PROGRESS, uProgressCur);进度是用uReadCount累加的,但实际应该用dwBytesWritten。另外,整个上传逻辑中的上传进度回调跟我描述的应该是一致的,都是在InternetWriteFile返回后就上报进度,同样存在不准确的问题吧。没有不准确,因为每次读写都是1024字节
    大大,比如发一个10MB的文件,每次InternetWriteFile 1024字节,但InternetWriteFile函数执行速度很快,立即返回,在do-while循环里很快便可将10MB的内容write到buffer中,这个时候按照代码逻辑,进度就已经是100%了,但实际代码运行到这里的时候,这10MB的数据还远没有通过网络发送到对端。我纠结的就是这个问题。劳烦大大指教,是不是我哪里用错了。