网上找了一堆代码,有用wininet的,还有用socket的,整理了半天,还是觉得socket靠谱。
只支持内存中断点续传。如果要加上在磁盘上断点续传,原理也差不多,不是本文重点。
注释:
1. CByteBufferVector是一个缓存池,动态分配BYTE形数组空间用的。代码略,可以简单看成BYTE数组。
2. GetStringA是一个CString转CStringA的函数,无需多说。
3. 除了win socket基本没有其它依赖,噢对,ATL::CString除外……
头文件:
class CSocketDownloader;
/**
* 下载任务
*/
class CDownloadTask
{
friend class CSocketDownloader;
public:
CDownloadTask();
CStringA GetUrlA() const;
CStringA GetAgnetA() const;
void ParseUrl();
int Percentage() const;
DWORD RemainTimeSec(DWORD dwTickElapsed, unsigned int uBytesTransferred) const;
CString m_strUrl; // 下载地址
CString m_strAgent; // 用户agent
int m_nMaxTryCount; // 最多重试次数(重定向不算重试,默认20次)
int m_nTimeoutSec; // socket超时(秒,默认10秒)
int m_nPort; // 端口(默认80)
HWND m_hWnd; // 接收下载进度消息的窗口句柄
LONG *m_pTerminate; // 指向是否中止的标志位,一般由用户界面操作(如点击“取消”按钮)更改此值
protected:
CStringA m_strAbsoluteUrlA;
CStringA m_strQueryA;
CStringA m_strHostA;
unsigned int m_uReadBytes;
unsigned int m_uTotalBytes;
};
/**
* socket实现的断点续传下载器
*/
class CSocketDownloader
{
public:
CSocketDownloader();
virtual ~CSocketDownloader();
// 下载到一个buffer
DWORD DownloadToBuffer(CDownloadTask &task, CByteBufferVector &bufVec);
// 下载到一个文件
DWORD DownloadToFile(CDownloadTask &task, CString strOutputFile);
protected:
DWORD DoDownloadToBuffer(CDownloadTask &task, CByteBufferVector &bufVec);
DWORD ConnectServer(const CDownloadTask &task, SOCKET hSocket);
DWORD DoDownloadToBufferInner(CDownloadTask &task, CByteBufferVector &bufVec, SOCKET hSocket);
int GetSleepSecCount(int nTryCount) const;
int GetBufferSize(const CDownloadTask &task) const;
CStringA GenerateRequest(CDownloadTask &task) const;
};
CPP文件:
#include <math.h>
#include <time.h>
const int BLOCK_SIZE = 1024 * 64;
const int DEFAULT_MAX_TRY = 20;
const int DEFAULT_TIMEOUT = 10;
//////////////////////////////////////////////////////////////////////////
// 下载任务
//////////////////////////////////////////////////////////////////////////
CDownloadTask::CDownloadTask()
: m_nPort(INTERNET_DEFAULT_HTTP_PORT),
m_nMaxTryCount(DEFAULT_MAX_TRY),
m_uReadBytes(0),
m_uTotalBytes(0),
m_nTimeoutSec(DEFAULT_TIMEOUT),
m_hWnd(NULL),
m_pTerminate(NULL)
{
}
CStringA CDownloadTask::GetUrlA() const
{
return GetStringA(m_strUrl);
}
CStringA CDownloadTask::GetAgnetA() const
{
return GetStringA(m_strAgent);
}
void CDownloadTask::ParseUrl()
{
m_strAbsoluteUrlA = m_strHostA = m_strQueryA = "";
CStringA strUrlA = this->GetUrlA();