`
bardo
  • 浏览: 372688 次
  • 性别: Icon_minigender_1
  • 来自: 上海
博客专栏
D1407912-ab64-3e76-ae37-b31aa4afa398
浅述PHP设计模式
浏览量:11639
9d6df9f7-91da-3787-a37c-0e826525dd5d
Zend Framewor...
浏览量:9992
85b628bd-a2ed-3de2-a4b1-0d34985ae8b6
PHP的IDE(集成开发环...
浏览量:9348
社区版块
存档分类
最新评论

[转]VC++的INTERNET异步多线程下载的源码

    博客分类:
  • VC
阅读更多
[转]VC++的INTERNET异步多线程下载的源码 ( 2006-10-12 16:36 )


// 主线程工作流程
//创建下载子线程
  m_hMainThread = ::CreateThread(NULL,
   0,
   AsyncMainThread,
   this,
   NULL,
   &m_dwMainThreadID);

//等待子线程返回消息
  MSG msg;
  while (1)
  {
   ::GetMessage(&msg, m_hWnd, 0, 0);

   if (msg.message == WM_ASYNCGETHTTPFILE)
   { //子线程发回消息
    switch(LOWORD(msg.wParam))
    {
    case AGHF_FAIL:
     {
     MessageBox(_T("下载行动失败结束!"));
     return;
     }
    case AGHF_SUCCESS:
     MessageBox(_T("下载行动成功结束!"));
     return;
    case AGHF_PROCESS:
     //下载进度通知
     break;
    case AGHF_LENGTH:
     //获取下载文件尺寸通知
     break;
    }
   }

   DispatchMessage(&msg);
  }

// 下载子线程工作流程
// 使用标记 INTERNET_FLAG_ASYNC 初始化 InternetOpen
  m_hInternet = ::InternetOpen(m_szAgent,
   INTERNET_OPEN_TYPE_PRECONFIG,
   NULL,
   NULL,
   INTERNET_FLAG_ASYNC);


// 设置状态回调函数 InternetSetStatusCallback
  ::InternetSetStatusCallback(m_hInternet, AsyncInternetCallback);

  //重置回调函数设置成功事件
  ::ResetEvent(m_hEvent[0]);
  m_hCallbackThread = ::CreateThread(NULL,
   0,
   AsyncCallbackThread,
   this,
   NULL,
   &m_dwCallbackThreadID);
  //等待回调函数设置成功事件
  ::WaitForSingleObject(m_hEvent[0], INFINITE);
 
 
 
 
  //回调函数线程的实现如下:
  DWORD WINAPI CAsyncGetHttpFile::AsyncCallbackThread(LPVOID lpParameter)
  {
   CAsyncGetHttpFile * pObj = (CAsyncGetHttpFile*)lpParameter;
 
   ::InternetSetStatusCallback(pObj->m_hInternet, AsyncInternetCallback);
 
   //通知子线程回调函数设置成功,子线程可以继续工作
   ::SetEvent(pObj->m_hEvent[0]);
  
   //等待用户终止事件或者子线程结束事件
   //子线程结束前需要设置子线程结束事件,并等待回调线程结束
   ::WaitForSingleObject(pObj->m_hEvent[2], INFINITE);
   return 0;
  }


//打断一下子线程的流程,由于回调函数和上一部分的关系如此密切,我们来看看它的实现
  void CALLBACK CAsyncGetHttpFile::AsyncInternetCallback(
   HINTERNET hInternet,
   DWORD dwContext,
   DWORD dwInternetStatus,
   LPVOID lpvStatusInformation,
   DWORD dwStatusInformationLength)
  {
   CAsyncGetHttpFile * pObj = (CAsyncGetHttpFile*)dwContext;
   //在我们的应用中,我们只关心下面三个状态
   switch(dwInternetStatus)
   {
   //句柄被创建
   case INTERNET_STATUS_HANDLE_CREATED:
    pObj->m_hFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)
     (lpvStatusInformation))->dwResult);
    break;
   //句柄被关闭
   case INTERNET_STATUS_HANDLE_CLOSING:
    ::SetEvent(pObj->m_hEvent[1]);
    break;
   //一个请求完成,比如一次句柄创建的请求,或者一次读数据的请求
   case INTERNET_STATUS_REQUEST_COMPLETE:
    if (ERROR_SUCCESS == ((LPINTERNET_ASYNC_RESULT)
     (lpvStatusInformation))->dwError)
    { //设置句柄被创建事件或者读数据成功完成事件
     ::SetEvent(pObj->m_hEvent[0]);
    }
    else
    { //如果发生错误,则设置子线程退出事件
     //这里也是一个陷阱,经常会忽视处理这个错误,
     ::SetEvent(pObj->m_hEvent[2]);
    }
    break;
   }
  }


//继续子线程的流程,使用 InternetOpenUrl 完成连接并获取下载文件头信息
  //重置句柄被创建事件
  ::ResetEvent(m_hEvent[0]);
  m_hFile = ::InternetOpenUrl(m_hInternet,
   m_szUrl,
   NULL,
   NULL,
   INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD,
   (DWORD)this);
  if (NULL == m_hFile)
  {
   if (ERROR_IO_PENDING == ::GetLastError())
   {
    if (WaitExitEvent())
    {
     return FALSE;
    }
   }
   else
   {
    return FALSE;
   }
  }
  //等我们把 WaitExitEvent 函数的实现列出在来再解释发生的一切:
  BOOL CAsyncGetHttpFile::WaitExitEvent()
  {
   DWORD dwRet = ::WaitForMultipleObjects(3, m_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;
  }
 

//e. 使用 HttpQueryInfo 分析头信息
  DWORD dwStatusSize = sizeof(m_dwStatusCode);
  if (FALSE == ::HttpQueryInfo(m_hFile,
   HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
   &m_dwStatusCode,
   &dwStatusSize,
   NULL))  //获取返回状态码
  {
   return FALSE;
  }
  //判断状态码是不是 200
  if (HTTP_STATUS_OK != m_dwStatusCode)
  {
   return FALSE;
  }
  DWORD dwLengthSize = sizeof(m_dwContentLength);
  if (FALSE == ::HttpQueryInfo(m_hFile,
   HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
   &m_dwContentLength,
   &dwLengthSize,
   NULL))  //获取返回的Content-Length
  {
   return FALSE;
  }
  ...//通知主线程获取文件大小成功
  //需要说明的是 HttpQueryInfo 并不进行网络操作,因此它不需要进行异步操作的处理。

//f. 使用标记 IRF_ASYNC 读数据 InternetReadFileEx
  //为了向主线程报告进度,我们设置每次读数据最多 1024 字节
  for (DWORD i=0; i<m_dwContentLength; )
  {
   INTERNET_BUFFERS i_buf = {0};
   i_buf.dwStructSize = sizeof(INTERNET_BUFFERS);
   i_buf.lpvBuffer = new TCHAR[1024];
   i_buf.dwBufferLength = 1024;

   //重置读数据事件
   ::ResetEvent(m_hEvent[0]);
   if (FALSE == ::InternetReadFileEx(m_hFile,
    &i_buf,
    IRF_ASYNC,
    (DWORD)this))
   {
    if (ERROR_IO_PENDING == ::GetLastError())
    {
     if (WaitExitEvent())
     {
      delete[] i_buf.lpvBuffer;
      return FALSE;
     }
    }
    else
    {
     delete[] i_buf.lpvBuffer;
     return FALSE;
    }
   }
   else
   {
    //在网络传输速度快,步长较小的情况下,
    //InternetReadFileEx 经常会直接返回成功,
    //因此要判断是否发生了用户要求终止子线程事件。
    if (WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEvent[2], 0))
    {
     ::ResetEvent(m_hEvent[2]);
     delete[] i_buf.lpvBuffer;
     return FALSE;
    }
   }
   i += i_buf.dwBufferLength;
   ...//保存数据
   ...//通知主线程下载进度
   delete[] i_buf.lpvBuffer;
  }

  //关闭 m_hFile
  ::InternetCloseHandle(m_hFile);
  //等待句柄被关闭事件或者要求子线程退出事件
  while (!WaitExitEvent())
  {
   ::ResetEvent(m_hEvent[0]);
  }
  //设置子线程退出事件,通知回调线程退出
  ::SetEvent(m_hEvent[2]);
  //等待回调线程安全退出
  ::WaitForSingleObject(m_hCallbackThread, INFINITE);
  ::CloseHandle(m_hCallbackThread);
  //注销回调函数
  ::InternetSetStatusCallback(m_hInternet, NULL);
  ::InternetCloseHandle(m_hInternet);
  ...//通知主线程子线程成功或者失败退出
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics