在使用CInternetFile多线程下载HTTP服务器上文件的时候,最主要是关注两个问题:
1)下载线程同步的问题
2)如何读取并存储文件的问题
第一个问题我们使用CEvent类来解决线程同步,用CEvent类来创建数组,每个线程都拥有一个CEvent对象,并在每个线程函数的尾部调用CEvent类的SetEvent()函数设置为有信号,以表示该线程完成文件的下载。
第二个问题我们可以使用一个void类型的数组来存储每个线程下载的文件,所需要的内存空间用LocalAlloc函数来进行动态分配。
以下是主要实现代码,仅供参考:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 CString URL="http://www.sohu.com/filename.txt" //完整的文件网络路径
2 UINT m_ThreadCount; //线程数量
3 DWORD m_PerFileSize; //每个线程下载的文件大小
4 DWORD m_EndFileSize; //最后一个线程下载的文件大小
5
6 HANDLE* m_pHthread; //线程句柄
7
8 void** m_pData; //每个线程序下载数据的缓冲区,在每个线程均下载完成后合成一个完整的文件
9 CEvent* m_pEvent; //事件对象,用于线程同步
10 DWORD WINAPI ThreadProc(LPVOID lpParameter) //线程函数
11
12 void main()
13 {
14 CInternetSession * pSession = new CInternetSession; //使用CInternetSession类创建一个Internet会话
15 CHttpConnection * pFtpCon = pSession->GetHttpConnection("127.0.0.1"); //打开一个HTTP服务器,127.0.0.1指向本地计算机
16 CInternetFile* pFile = (CInternetFile*)pSession->OpenURL(URL,1,INTERNET_FLAG_TRANSFER_BINARY|INTERNET_FLAG_RELOAD); //打开网络文件,设置为二进制流和重新从服务器上下载
17 DWORD len = pFile->SeekToEnd(); //取得文件长度
18
19 m_PerFileSize= len / m_ThreadCount; //每个线程应下载的文件大小
20 m_EndFileSize = m_PerFileSize+ len % m_ThreadCount; //最后一个线程要下载的文件大小会<=其他线程
21
22 delete pFile;
23 delete pFtpCon;
24 delete pSession; //释放资源
25
26 int pos = URL.ReverseFind('/');
27
28 CString filename = URL.Right(URL.GetLength()-pos-1); //取得文件名称
29
30 for (int i = 0; i<m_ThreadCount; i++)
31 {
32 int* temp = new int ;
33 *temp = i;
34
35 m_pEvent[i].ResetEvent();
36 m_pHthread[i] = CreateThread(NULL,0,ThreadProc,temp,0,NULL);
37 }//创建多个事件和线程
38
39
40 for (int n = 0 ; n< m_ThreadCount; n++)
41 {
42 WaitForSingleObject(m_pEvent[n],INFINITE);
43 }//等待每个线程执行完毕
44
45 CFile file ("c:\\"+filename,CFile::modeCreate|CFile::modeWrite); //创建本地文件
46 DWORD readsize;
47
48 for (int m = 0; m<m_ThreadCount; m++)
49 {
50 if (m < m_ThreadCount-1)
51 readsize = m_PerFileSize;
52 else
53 readsize = m_EndFileSize;
54 file.Write(m_pData[m],readsize);
55 }//写入文件
56 delete[] m_pData;
57 m_pData = new void*[m_ThreadCount];
58 }
59
60 //线程函数
61 DWORD WINAPI ThreadProc(LPVOID lpParameter )
62 {
63 int index = *(int*)lpParameter; //线程ID
64 delete lpParameter;
65 CInternetSession * pSession = new CInternetSession;
66 CHttpConnection * pFtpCon = pSession->GetHttpConnection("127.0.0.1");
67 CInternetFile* pFile = (CInternetFile*)pSession->OpenURL(URL,1,INTERNET_FLAG_TRANSFER_BINARY|INTERNET_FLAG_RELOAD);
68
69 DWORD readsize;
70 if (index < m_ThreadCount-1)
71 readsize = m_PerFileSize;
72 else
73 readsize = m_EndFileSize; //查询本线程要下载数据量
74
75 pFile->Seek(index*m_PerFileSize,FILE_BEGIN); //定位到要下载那一段数据的头部
76 m_pData[index] = LocalAlloc(LMEM_FIXED,readsize); //分配适当内存
77 pFile->Read(m_pData[index],readsize); //读取二进制流,即下载
78
79 delete pFile;
80 delete pFtpCon;
81 delete pSession; //释放资源
82
83 m_pEvent[index].SetEvent(); //设置CEvent为有信号,表示本段数据下载完成
84 return 0;
85 }
以上代码还有很多可以改进的地方,而且还没有错误提示,核心的代码就是这些,请各位看官各自完善。