当前位置:首页 > 开发 > 编程语言 > 多线程 > 正文

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

发表于: 2010-04-25   作者:bardo   来源:转载   浏览次数:
摘要: [转]VC++的INTERNET异步多线程下载的源码 ( 2006-10-12 16:36 ) // 主线程工作流程 //创建下载子线程   m_hMainThread = ::CreateThread(NULL,    0,    AsyncMainThread,    this,  &nbs
[转]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);
  ...//通知主线程子线程成功或者失败退出

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

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
实现的功能:1)演示多线程NSThread开发;2)子线程中执行下载图片工作,图片下载完成前显示等待框;
从Internet获取数据 利用HttpURLConnection对象,我们可以从网络中获取网页数据. URL url = new URL(
实现的功能:1)演示多线程NSOperation&NSOperationQueue开发;2)子线程中执行下载图片工作,图片下
Struts做文件下载时,遇到如图情况 原因之一:中文文件名 <global-results> <result name="
  最近很忙,因此拿出时间来写博客也算是忙里偷闲了,继承前面的一贯风格,继续浅谈胡侃。   最
1、首先你的电脑要安装好git,这个请参考git官网。 2、打开git命令窗口输入git clone http://androi
异步下载支持应用程序在后台下载数据,在等待下载完成的过程中不会阻塞代码的运行,异步连接模型图
public delegate int SumDelegate(int i,int j); public class AsyncDelegate { private const int
趁着期末考,顺道把多线程和异步的给整整。 1、多线程: NET多线程操作的话是在 using System.Thread
1、多线程: NET多线程操作的话是在 using System.Threading; 创建线程的第一种方法:ThreadStart wo
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号