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

[转]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);
  ...//通知主线程子线程成功或者失败退出
分享到:
评论

相关推荐

    VC++ TCP_IP线程池IOCP模式下的封装库源码.rar

    线程池是一种多线程编程的设计模式,它预先创建了一组线程,当有任务需要执行时,线程池会分配一个空闲线程来执行任务,而不是每次任务到来时都创建新的线程。这可以减少线程的创建和销毁带来的开销,提高系统效率。...

    VC++源码保存网页为MHT文件(包含图片及其它资源)

    标题 "VC++源码保存网页为MHT文件(包含图片及其它资源)" 描述了如何使用Microsoft Visual C++(VC++)编程语言来实现一个功能,即抓取网页内容并将其保存为单一文件的MHT(MHTML,Multipurpose Internet Mail ...

    vc++ 开发实例源码包

    FTP、HTTP 多线程断点续传下载文件 源码 如题。 gdiplus应用实例 如题,自绘控件的实现。 gdiplus应用实例2 如题,自绘控件的实现。 GetFileVersion 这个例子就是查询任何可执行文件的版本信息并且 C++builder 和...

    极速FTP客户端程序(VC++版源码)

    【描述】提到的是"极速",这暗示了该FTP客户端可能实现了高效的文件传输,可能采用了多线程技术、异步I/O或者缓存策略来提升速度。在C++中,这通常涉及到Windows API的套接字编程,如WSAStartup、connect、send、...

    VC++网络程序设计第二章源码

    8. **多线程编程**:在服务器端,为了同时处理多个客户端连接,可能会引入多线程概念,使用`CreateThread`创建新线程来处理每个新的连接。 9. **异步I/O**:了解IOCP(I/O完成端口)的概念,它是Windows上进行高...

    基于VC++平台上实现以太网通讯,包括VC-TCP-Server服务器端的实现和VC-TCP-Client客户端的实现源码

    6. **多线程处理**:在服务器端,为了处理多个并发连接,通常需要使用多线程技术,每个新连接创建一个新的线程进行处理,或者使用IOCP等异步I/O模型,避免阻塞主线程。 7. **异常处理**:在网络通信中,错误处理...

    VC++多客户连接通讯例程

    - **多线程处理**:为了同时处理多个客户端的连接请求,服务器通常会为每个新连接创建一个新的线程或使用异步I/O模型。 - **并发连接管理**:服务器需要维护一个连接列表,记录所有已建立连接的客户端,以便进行...

    IOCP的Socket 服务器端源码

    **IOCP(I/O Completion Port)是Windows操作系统中的一种高效I/O模型,它在多线程环境下用于处理大量并发I/O操作。IOCP的核心在于将I/O操作与通知机制分离,使得系统能以非阻塞的方式处理I/O请求,极大地提高了...

    网络典型应用实例vc++6.0

    3. **多线程编程**:在网络编程中,多线程可以提高并发处理能力,例如,一个线程用于接收客户端连接,另一个线程负责处理数据传输。 4. **异步I/O**:在VC++6.0中,可以使用异步套接字函数,如WSAAsyncSelect或...

    VC 快速查找局域网内所有机器的程序源码

    本文将详细解析"VC 快速查找局域网内所有机器的程序源码"这一主题,主要关注如何利用多线程技术和网络扫描技术来实现这一功能。 首先,"VC"在这里指的是Visual C++,微软开发的一款集成开发环境,支持C++编程语言。...

    windows网络与通信程序设计源码

    在Windows平台上进行网络与通信程序设计,涉及到许多关键知识点,主要涵盖了网络协议、API调用、多线程编程以及数据传输等。以下是对这些领域的详细解释: 1. **网络协议**:网络通信的基础是各种网络协议,如TCP/...

    vc++Ping命令模拟进阶----如何嗅探某个IP段

    这涉及到网络编程、多线程、异步I/O等知识。 首先,我们要理解Ping命令的工作原理。它基于ICMP(Internet Control Message Protocol,互联网控制消息协议)的回显请求和应答机制。在Windows环境下,我们可以调用...

    irc-server:用C ++实现成熟的IRC服务器

    C++可以利用多线程或多进程模型来实现并发,或者使用异步I/O模型如boost.asio库来提高性能。 5. **状态管理和会话**:服务器需要跟踪每个用户的状态(如是否在线,所在的频道等),这通常通过维护一个用户和频道的...

    COM与COM+从入门到精通(pdf版本,含源码)

    Win32多线程应用 线程COM组件 自动化与IDispatch 用VC++实现IDispatch ATL与自动化 Automation数据类型 再谈类型库 C++自动化客户机 VB自动化客户机 小结 第3章 用VB建立并使用COM服务器 选择COM...

    网络编程实用教程(第三版).zip

    7.3 VC++对多线程网络编程的支持 192 7.3.1 MFC支持的两种线程 192 7.3.2 创建MFC的工作线程 193 7.3.3 创建并启动用户界面线程 195 7.3.4 终止线程 198 7.4 多线程FTP客户端实例 200 7.4.1 编写线程...

    UDP 文件传输vc源代码

    在VC++中,可以使用`CreateThread`或者`async`关键字来实现多线程或异步操作。 7. **文件I/O**:在接收端,接收的数据需要写入到本地文件中。VC++提供了标准库`fstream`来处理文件的读写操作,如`ofstream`用于写...

    局域网内聊天及文件传输源代码

    3. 多线程处理:为了实现异步通信和并行处理,可能会用到多线程技术。 4. 用户界面更新:当收到新消息或文件时,UI需要实时更新以显示这些信息。 通过分析和学习这个项目,你可以掌握网络编程中的基本原理和技巧,...

    VISUAL C MFC扩展编程实例与源码

    15.3 实例62:创建VC++宏 350 15.4 实例63:使用函数地址 351 15.5 实例64:二进制字符串 352 15.6 实例65:重新启动计算机 356 15.7 实例66:获得可用磁盘空间 357 15.8 实例67:闪烁窗口和文本 358

    VC实现ping对端的功能

    - **性能优化**:如果需要频繁进行ping操作,可以考虑使用多线程或者异步IO来提高效率。 - **结果解析**:收到ICMP回显应答后,需要解析报文以获取如往返时间、数据包大小等信息。 总的来说,实现VC++的ping功能...

    C事件驱动循环的网络IO编程框架源代码

    它们允许程序在一个线程中等待多个I/O操作的完成。 3. **非阻塞I/O**:避免因等待I/O操作完成而阻塞主线程,提高系统吞吐量。 4. **异步I/O**:如Windows的`OVERLAPPED`结构和`Begin/EndSend/Receive`,在后台处理I/...

Global site tag (gtag.js) - Google Analytics