用wininet进行文件的http上传下载如何精准获取传输进度

chenkegarfield 2013-11-01 03:46:37
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说多少多少字节我收到了。

请求各位大侠帮忙~~跪谢~~~
...全文
987 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
chenkegarfield 2013-11-01
  • 打赏
  • 举报
回复
引用 5 楼 xiaoc1026 的回复:
[quote=引用 4 楼 chenkegarfield 的回复:] [quote=引用 3 楼 xiaoc1026 的回复:]
谢谢这位大大,有两个疑问。 发送文件内容的逻辑中 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返回后就上报进度,同样存在不准确的问题吧。[/quote] 没有不准确,因为每次读写都是1024字节[/quote] 大大,比如发一个10MB的文件,每次InternetWriteFile 1024字节,但InternetWriteFile函数执行速度很快,立即返回,在do-while循环里很快便可将10MB的内容write到buffer中,这个时候按照代码逻辑,进度就已经是100%了,但实际代码运行到这里的时候,这10MB的数据还远没有通过网络发送到对端。我纠结的就是这个问题。 劳烦大大指教,是不是我哪里用错了。
见习学术士 2013-11-01
  • 打赏
  • 举报
回复
引用 4 楼 chenkegarfield 的回复:
[quote=引用 3 楼 xiaoc1026 的回复:]
谢谢这位大大,有两个疑问。 发送文件内容的逻辑中 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返回后就上报进度,同样存在不准确的问题吧。[/quote] 没有不准确,因为每次读写都是1024字节
chenkegarfield 2013-11-01
  • 打赏
  • 举报
回复
引用 3 楼 xiaoc1026 的回复:
谢谢这位大大,有两个疑问。 发送文件内容的逻辑中 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返回后就上报进度,同样存在不准确的问题吧。
见习学术士 2013-11-01
  • 打赏
  • 举报
回复


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;
}

chenkegarfield 2013-11-01
  • 打赏
  • 举报
回复
引用 1 楼 oyljerry 的回复:
不然就是用socket来做,这样可以很好的控制进度等
http的方式应该是能做到的。socket的方式肯定最直接和准确,只不过整个程序也不是专业的下载软件,已经封装了一套http库,暂时没打算换做socket方式去实现。 求教wininet是否支持flush操作呢?
oyljerry 2013-11-01
  • 打赏
  • 举报
回复
不然就是用socket来做,这样可以很好的控制进度等

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧