http://www.codeproject.com/KB/IP/asyncwininet.aspx#
http://support.microsoft.com/kb/839873
do not ask me why.i dont know
FILE: AsyncWinINet.h
#include <wininet.h>
#include <mmsystem.h>
class AsyncWinINet
{
public:
typedef void (*notify_fp)(const StringMap&);
class thread_info
{
public:
thread_info(const String& _url, //请求下载的地址(in)
const StringMap& _request_headrs, //请求头request_headrs(in)
const notify_fp& _pfp, //下载进度通知回调函数指针
const StringMap& _pfp_param,
String& _response_headrs, //返回头response_headrs(out)
const String& _saved_filename, //下载内容保存文件名(in)
String& _response_content, //返回内容(out)
size_t _read_content_size) //控制保存在response_content中内容的长度(in)) :
: request_headrs(_request_headrs), pfp(_pfp),
pfp_param(_pfp_param), //pfp函数传回参数
response_headrs(_response_headrs), saved_filename(_saved_filename),
response_content(_response_content), read_content_size(_read_content_size)
{
this->response_headrs.clear();
this->response_content.clear();
this->url = StringUtil::EncodeURIComponent(_url);
for(int i = 0; i < 3; ++i)
{
this->hEvent[i] = CreateEvent(NULL,TRUE,FALSE,NULL);
}
}
HANDLE hThread;
DWORD dwThreadID;
HANDLE hCallbackThread;
DWORD dwCallbackThreadID;
HANDLE hEvent[3];
LPVOID hInternet;
LPVOID hFile;
DWORD dwStatusCode;
DWORD dwContentLength;
String url; //请求下载的地址(in)
const StringMap& request_headrs; //请求头request_headrs(in)
const notify_fp& pfp; //下载进度通知回调函数指针
const StringMap& pfp_param; //pfp函数传回参数
String& response_headrs; //返回头response_headrs(out)
const String& saved_filename; //下载内容保存文件名(in)
String& response_content; //返回内容(out)
size_t read_content_size; //控制保存在response_content中内容的长度(in)
};
/*******************************************************************************
* 函数:download
* 功能:下载,返回WinINet_ERR_CODE值
* 说明:关于notify_fp 类型说明: 函数的参数为StringMap类型,传回的变量名与变量值
* 2007-12
*******************************************************************************/
static DWORD download(const String& url, //请求下载的地址(in)
const StringMap& request_headrs, //请求头request_headrs(in)
const notify_fp& pfp, //下载进度通知回调函数指针
const StringMap& pfp_param, //pfp函数传回参数
String& response_headrs, //返回头response_headrs(out)
const String& saved_filename, //下载内容保存文件名(in)
String& response_content, //返回内容(out)
size_t read_content_size = 0); //控制保存在response_content中内容的长度(in)
protected:
static BOOL WaitExitEvent(thread_info *p);
static DWORD WINAPI AsyncThread(LPVOID lpParameter);
static DWORD WINAPI AsyncCallbackThread(LPVOID lpParameter);
static VOID CALLBACK AsyncInternetCallback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength);
};
FILE: AsyncWinINet.cpp
DWORD AsyncWinINet::download(const string &url, const map &request_headrs,
const AsyncWinINet::notify_fp &pfp, const map &pfp_param, string &response_headrs,
const string &saved_filename, string &response_content, size_t read_content_size)
{
thread_info info(url, request_headrs, pfp,
pfp_param, response_headrs, saved_filename,
response_content, read_content_size);
info.hThread = CreateThread(NULL,
0,
AsyncWinINet::AsyncThread,
&info,
NULL,
&info.dwThreadID);
WaitForSingleObject(info.hThread, INFINITE); //等待子线程安全退出
CloseHandle(info.hThread);//关闭线程句柄
return TRUE;
}
//---------------------------------------------------------------------
DWORD WINAPI AsyncWinINet::AsyncThread(LPVOID lpParameter)
{
thread_info* p = (thread_info*)lpParameter;
//a. 使用标记 INTERNET_FLAG_ASYNC 初始化 InternetOpen
string user_agent("Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler ; .NET CLR 2.0.50727)");
map iheadrs(p->request_headrs.begin(), p->request_headrs.end());
map::iterator it = iheadrs.find("User-Agent");
if(it == iheadrs.end()) iheadrs["User-Agent"] = user_agent;
else user_agent = it->second;
p->hInternet = InternetOpen(user_agent.c_str(),
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
INTERNET_FLAG_ASYNC);
//ResetEvent(p->hEvent[0]);
//p->hCallbackThread = CreateThread(NULL,
// 0,
// AsyncWinINet::AsyncCallbackThread,
// p,
// NULL,
// &p->dwCallbackThreadID);
//WaitForSingleObject(p->hEvent[0], INFINITE);//等待回调函数设置成功事件
InternetSetStatusCallback(p->hInternet, AsyncWinINet::AsyncInternetCallback);
string sheadrs;
for(it = iheadrs.begin(); it != iheadrs.end(); ++it)
{
sheadrs += it->first + ":" + it->second;
if(it->second.find(StringUtil::enter) == string::npos) { sheadrs += StringUtil::enter; }
}
sheadrs += StringUtil::enter;
DWORD start_time = timeGetTime();
ResetEvent(p->hEvent[0]); //重置句柄被创建事件
p->hFile = InternetOpenUrl(p->hInternet, p->url.c_str(), sheadrs.c_str(), sheadrs.length(),
INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, (DWORD)p);
FILE *fp = fopen(p->saved_filename.c_str(), "w+");
while(true)
{
if (NULL == p->hFile)
{
DWORD dwError = ::GetLastError();
if (ERROR_IO_PENDING == dwError || ERROR_SUCCESS == dwError)
{
if (WaitExitEvent(p)) { break; }
}
else break;
}
//读取返回文件头
DWORD dwLength = 0;
LPVOID lpOutBuffer = NULL;
while(true) //读取response_headrs数据
{
if(!HttpQueryInfo(p->hFile, HTTP_QUERY_RAW_HEADERS_CRLF,
lpOutBuffer, &dwLength, NULL))
{
DWORD err_code = GetLastError();
if (err_code == ERROR_HTTP_HEADER_NOT_FOUND) break;
else if(err_code == ERROR_INSUFFICIENT_BUFFER)
{
lpOutBuffer = new char[dwLength];
continue;
}
else break;
}
break;
}
if(lpOutBuffer != NULL)
{
p->response_headrs.append((char*)lpOutBuffer,dwLength);
delete [] lpOutBuffer;
}
//e. 使用 HttpQueryInfo 分析头信息 HttpQueryInfo 使用非阻塞方式,所以不用等待
DWORD dwStatusSize = sizeof(p->dwStatusCode);
if (FALSE == HttpQueryInfo(p->hFile, //获取返回状态码
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
&p->dwStatusCode, &dwStatusSize, NULL)) { break; }
//判断状态码是不是 200
if (HTTP_STATUS_OK != p->dwStatusCode) break;
map msgMap(p->pfp_param.begin(), p->pfp_param.end());
msgMap["url"] = p->url;
//获取返回的Content-Length
//DWORD dwLengthSize = sizeof(p->dwContentLength);
//if (FALSE == HttpQueryInfo(p->hFile,
//HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
//&p->dwContentLength, &dwLengthSize, NULL)) { p->dwContentLength = 0; }
//f. 使用标记 IRF_ASYNC 读数据 InternetReadFileEx
//为了向主线程报告进度,我们设置每次读数据最多 1024 字节
char lpvBuffer[1024];
p->dwContentLength = 0; //Content-Length: 202749
while(true)
{
INTERNET_BUFFERS i_buf = {0};
i_buf.dwStructSize = sizeof(INTERNET_BUFFERS);
i_buf.lpvBuffer = lpvBuffer;
i_buf.dwBufferLength = 1024;
//重置读数据事件
ResetEvent(p->hEvent[0]);
if (FALSE == InternetReadFileEx(p->hFile, &i_buf, IRF_ASYNC, (DWORD)p))
{
if (ERROR_IO_PENDING == ::GetLastError())
{
if (WaitExitEvent(p)) break;
}
else break;
}
else
{
//在网络传输速度快,步长较小的情况下,InternetReadFileEx 经常会直接返回成功,
//因此要判断是否发生了用户要求终止子线程事件。
if (WAIT_OBJECT_0 == WaitForSingleObject(p->hEvent[2], 0))
{
ResetEvent(p->hEvent[2]);
break;
}
}
if(i_buf.dwBufferLength == 0)
{
DWORD time = timeGetTime() - start_time;
if(time != 0)
{
Real speed = (Real)p->dwContentLength;
speed /= ((Real)time)/1000.0f;
speed /= 1024.0f;
msgMap["speed"] = StringUtil::toString((DWORD)speed);
}
if(p->pfp) p->pfp(msgMap);
break;
}
if(fp)
{
fwrite(i_buf.lpvBuffer, sizeof(char), i_buf.dwBufferLength, fp);
}
if(p->read_content_size > p->response_content.size())
{
p->response_content.append((char*)i_buf.lpvBuffer, i_buf.dwBufferLength);
}
p->dwContentLength += i_buf.dwBufferLength;
}
break;
}
if(fp)
{
fflush(fp); fclose(fp); fp = NULL;
}
if(p->hFile)
{
InternetCloseHandle(p->hFile);//关闭 m_hFile
while (!WaitExitEvent(p)) //等待句柄被关闭事件或者要求子线程退出事件
{
ResetEvent(p->hEvent[0]);
}
}
//设置子线程退出事件,通知回调线程退出
SetEvent(p->hEvent[2]);
//等待回调线程安全退出
//WaitForSingleObject(p->hCallbackThread, INFINITE);
//CloseHandle(p->hCallbackThread);
//注销回调函数
InternetSetStatusCallback(p->hInternet, NULL);
InternetCloseHandle(p->hInternet);
return TRUE;
}
//------------------------------------------------------------------------------------
DWORD WINAPI AsyncWinINet::AsyncCallbackThread(LPVOID lpParameter)
{
thread_info *p = (thread_info*)lpParameter;
InternetSetStatusCallback(p->hInternet, AsyncWinINet::AsyncInternetCallback);
//通知子线程回调函数设置成功,子线程可以继续工作
SetEvent(p->hEvent[0]);
//等待用户终止事件或者子线程结束事件
//子线程结束前需要设置子线程结束事件,并等待回调线程结束
WaitForSingleObject(p->hEvent[2], INFINITE);
return 0;
}
//----------------------------------------------------------------------------
VOID CALLBACK AsyncWinINet::AsyncInternetCallback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength)
{
thread_info* p = (thread_info*)dwContext;
//在我们的应用中,我们只关心下面三个状态
switch(dwInternetStatus)
{
//句柄被创建
case INTERNET_STATUS_HANDLE_CREATED:
p->hFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)
(lpvStatusInformation))->dwResult);
break;
//句柄被关闭
case INTERNET_STATUS_HANDLE_CLOSING:
SetEvent(p->hEvent[1]);
break;
//一个请求完成,比如一次句柄创建的请求,或者一次读数据的请求
case INTERNET_STATUS_REQUEST_COMPLETE:
if (ERROR_SUCCESS == ((LPINTERNET_ASYNC_RESULT)
(lpvStatusInformation))->dwError)
{
//设置句柄被创建事件或者读数据成功完成事件
SetEvent(p->hEvent[0]);
}
else
{
//如果发生错误,则设置子线程退出事件 这里也是一个陷阱,经常会忽视处理这个错误,
SetEvent(p->hEvent[2]);
}
break;
case INTERNET_STATUS_CONNECTION_CLOSED:
SetEvent(p->hEvent[2]);
break;
}
}
//--------------------------------------------------------------------
BOOL AsyncWinINet::WaitExitEvent(thread_info *p)
{
DWORD dwRet = WaitForMultipleObjects(3, p->hEvent, FALSE, INFINITE);
switch (dwRet)
{
case WAIT_OBJECT_0://句柄被创建事件或者读数据请求成功完成事件
case WAIT_OBJECT_0+1://句柄被关闭事件
case WAIT_OBJECT_0+2://用户要求终止子线程事件或者发生错误事件
break;
}
return WAIT_OBJECT_0 != dwRet;
}
//A Example: Main.cpp
#include <iostream>
#include <cassert>
#include <Windows.h>
#include <wininet.h>
#include <tchar.h>
using namespace std;
DWORD dwNumKSent;
DWORD dwNumKToSend;
DWORD dwNumBytesComplete = 0;
char lpOutBuf[1024];
HANDLE hConnectedEvent, hRequestCompleteEvent;
HINTERNET m_Session, hConnect, hRequest, hFile;
char *lpszUrl, *lpszServer;
BOOL bAllDone = FALSE;
void __stdcall Callback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpStatusInfo,
DWORD dwStatusInfoLen);
void main(int argc, char *argv[])
{
if (argc != 4)
{
cout << "Usage: TestLib <server> <url> <size in kilobytes>" << endl;
cout << " Example: TestLib www.foo.com /postfolder/upload.exe 256" << endl;
return;
}
lpszServer = argv[1];
lpszUrl = argv[2];
dwNumKToSend = atoi(argv[3]);
// 设置缓冲及事件
FillMemory(lpOutBuf, 1024, 'A');
hConnectedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hRequestCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 打开一个Internet会话
m_Session = InternetOpen("sendreqexasync",
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
INTERNET_FLAG_ASYNC);
if (m_Session == NULL)
{
cout << "InternetOpen failed, error " << GetLastError();
return;
}
// 设置回调函数
if (InternetSetStatusCallback(m_Session,
(INTERNET_STATUS_CALLBACK)&Callback) == INTERNET_INVALID_STATUS_CALLBACK)
{
cout << "InternetSetStatusCallback failed, error " << GetLastError();
return;
}
// 开一个远程文件,包括连接和请求
hConnect = InternetOpenUrl(m_Session,
"http://192.168.1.103/myServer/RemoteConfig.xml",
NULL,
0,
INTERNET_FLAG_RELOAD |
INTERNET_FLAG_PRAGMA_NOCACHE |
INTERNET_FLAG_NO_CACHE_WRITE,
1);
if (hConnect == NULL)
{
if (GetLastError() != ERROR_IO_PENDING)
{
cout << "InternetConnect failed, error " << GetLastError();
return;
}
WaitForSingleObject(hConnectedEvent, INFINITE);
}
// 创建一个可读写文件
HANDLE fp = CreateFile("test.xml",GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,/*CREATE_NEW|*/OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,0);
cout << "------------------- Read the response -------------------" << endl;
while(true) //读取response_headrs数据
{
//WaitForSingleObject(hRequestCompleteEvent,INFINITE);
//读取返回文件头
DWORD dwLength = 0;
LPVOID lpOutBuffer = NULL;
while(true)
{
if(!HttpQueryInfo(hConnect, HTTP_QUERY_RAW_HEADERS_CRLF,
lpOutBuffer, &dwLength, NULL))
{
DWORD err_code = GetLastError();
if (err_code == ERROR_HTTP_HEADER_NOT_FOUND) break;
else if(err_code == ERROR_INSUFFICIENT_BUFFER)
{
lpOutBuffer = new char[dwLength];
continue;
}
else break;
}
break;
}
//e. 使用 HttpQueryInfo 分析头信息 HttpQueryInfo 使用非阻塞方式,所以不用等待
DWORD dwStatusSize = sizeof(DWORD);
DWORD dwServerBackState = 0;
if (FALSE == HttpQueryInfo(hConnect, //获取返回状态码
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
&dwServerBackState, &dwStatusSize, NULL)) { break; }
//判断状态码是不是 200
if (HTTP_STATUS_OK != dwServerBackState) break;
// 循环读入数据
int dwContentLength = 0;
DWORD dwFilewrite = 0;
while(true)
{
INTERNET_BUFFERS i_buf = {0};
i_buf.dwStructSize = sizeof(INTERNET_BUFFERS);
i_buf.lpvBuffer = lpOutBuf;
i_buf.dwBufferLength = 1024;
//重置读数据事件
ResetEvent(hConnectedEvent);
if (FALSE == InternetReadFileEx(hConnect, &i_buf, IRF_ASYNC, 2))
{
if (ERROR_IO_PENDING == ::GetLastError())
{
WaitForSingleObject(hConnectedEvent, INFINITE);
}
else break;
}
else
{
//在网络传输速度快,步长较小的情况下,InternetReadFileEx 经常会直接返回成功,
//因此要判断是否发生了用户要求终止子线程事件。
if (WAIT_OBJECT_0 == WaitForSingleObject(hConnectedEvent, 0))
{
ResetEvent(hConnectedEvent);
if(0==WriteFile(fp,lpOutBuf,i_buf.dwBufferLength,&dwFilewrite,NULL))
cout << "WriteFile failed, error " << GetLastError() <<endl;
CloseHandle(fp);
fp = NULL;
exit(0);
}
}
if(fp)
{
if(0==WriteFile(fp,lpOutBuf,i_buf.dwBufferLength,&dwFilewrite,NULL))
cout << "WriteFile failed, error " << GetLastError() <<endl;
}
dwContentLength += i_buf.dwBufferLength;
}
}
cout << endl << endl << "------------------- Request Complete ----------------" << endl;
system("pause");
}
void __stdcall Callback(HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpStatusInfo,
DWORD dwStatusInfoLen)
{
cout << "Callback dwInternetStatus: " << dwInternetStatus << " Context: " << dwContext << endl;
cout.flush();
switch(dwContext)
{
case 1: // Connection handle
if (dwInternetStatus == INTERNET_STATUS_HANDLE_CREATED)
{
INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
hConnect = (HINTERNET)pRes->dwResult;
cout << "Connect handle created" << endl;
cout.flush();
//SetEvent(hConnectedEvent);
}
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
{
cout << "Function call finished" << endl;
cout.flush();
SetEvent(hConnectedEvent);
}
if (dwInternetStatus == INTERNET_STATUS_CONNECTION_CLOSED)
{
cout << "Connect handle closed" << endl;
cout.flush();
SetEvent(hConnectedEvent);
}
break;
case 2: // Request handle
switch(dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CREATED:
{
INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
hRequest = (HINTERNET)pRes->dwResult;
cout << "Request handle created" << endl;
cout.flush();
}
break;
case INTERNET_STATUS_REQUEST_SENT:
{
DWORD *lpBytesSent = (DWORD*)lpStatusInfo;
cout << "Bytes Sent: " << *lpBytesSent << endl;
dwNumBytesComplete += *lpBytesSent;
}
break;
case INTERNET_STATUS_REQUEST_COMPLETE:
{
INTERNET_ASYNC_RESULT *pAsyncRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
cout << "Function call finished" << endl;
cout << "dwResult: " << pAsyncRes->dwResult << endl;
cout << "dwError: " << pAsyncRes->dwError << endl;
cout.flush();
SetEvent(hRequestCompleteEvent);
}
break;
case INTERNET_STATUS_RECEIVING_RESPONSE:
cout << "Receiving Response" << endl;
cout.flush();
//SetEvent(hRequestCompleteEvent);
break;
case INTERNET_STATUS_RESPONSE_RECEIVED:
{
DWORD *dwBytesReceived = (DWORD*)lpStatusInfo;
cout << "Received " << *dwBytesReceived << endl;
cout.flush();
}
}
}
}