http 断点续传,Windows下HTTP方式单线程下载
http 断点续传
www.diybl.com 时间 : 2011-05-20 作者:匿名 编辑:hawk 点击: 1128 [ 评论 ]
-
-
原理:
1. 打开本地文件fopen,移动文件指针到文件尾fseek
2. 获得文件大小ftell, 格式化HTTP请求头 "Range: bytes=ftell -", 以偏移httpfile指针,实现断点续传
3. 获得要下载的文件,以二进制形式传输,OpenURL
4. 接收数据,防止阻塞PeekMessage
Sample:
显示代码打印
01 char string[25];
02 CString StrFileName=m_lf;
03 CString StrUrl=m_url;
04 CString sheader;
05 long lStartPos =0;
06
07 //打开本地文件,m_lf 是文件路径,如:C:\\a.mp3
08 FILE *file=NULL;
09 file=fopen(m_lf,"ab");
10 if(file!=NULL)
11 {
12 //移动文件指针到文件尾
13 fseek(file,0,SEEK_END);
14 lStartPos=ftell(file);
15 itoa(lStartPos,string,10);
16 m_byte=string;
17 //格式化请求头,如: 从第 1000 个字节起开始下载:“Range: bytes=999 -”
18 sheader.Format(_T("%sRange: bytes=%d-\r\n"), szHeaders,lStartPos);
19 }
20 else
21 {
22 MessageBox("local file failed!");
23 return;
24 }
25 try{
26 CInternetSession s;
27 CHttpFile *hf=NULL;
28 //获得要下载的文件,以二进制形式传输
29 hf=(CHttpFile*)s.OpenURL(m_url,1,INTERNET_FLAG_TRANSFER_BINARY,sheader,-1 );
30 if(hf!=NULL)
31 {
32 //开始传输数据
33 byte *nbytes = new byte[512];
34 int nReadSize=0;
35 nReadSize=hf->Read(nbytes,512);
36 while( nReadSize >0)
37 {
38 fwrite(nbytes,1,nReadSize,file);
39 nReadSize=hf->Read(nbytes,512);
40 lStartPos=ftell(file);
41 itoa(lStartPos,string,10);
42 m_byte=string;
43 //防止阻塞
44 doenents();
45 }
46
47 fclose(file);
48 hf->Close();
49 delete hf;
50 }
51 }catch(CInternetException *p){
52 // hf=NULL;
53 p->Delete();
54 }
55 BOOL CGfDlg::doenents()
56 {
57 MSG msg;
58 while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
59 {
60 if(!AfxGetApp()->PumpMessage())
61 {
62 ::PostQuitMessage(0);
63 return false;
64 }
65 }
66 return true;
67 }
文章出处:飞诺网(www.diybl.com):http://www.diybl.com/course/3_program/c++/cppjs/20090409/164681.html
但是因为当时用了CFile类实现,而不是SDK,所以我不得不重写,重写时断点续传又重新成了问题
1.下载的是文件列表
2.因为效率不再采用分块传输
3.如何记录文件列表与断点值
鉴于我以前做的断点续传,思路被禁固了一样 转不开弯 直到后来KING老大提示才饶过这个弯 其思路如下
1.客户端用CreateFile以OPEN_EXISTING方式打开要下载的文件列表
2.若成功说明有断点文件,则用GetFileSize得到大小做为断点
3若失败说明文件不存在,则创建一个文件
思路是相当简单的,而且一个好处是不用记录断点值 干净利落
文章出处:飞诺网(www.diybl.com):http://www.diybl.com/course/3_program/c++/cppjs/2007114/83747.html
源码: Windows下HTTP方式单线程下载
www.diybl.com 时间 : 2008-03-31 作者:佚名 编辑:本站 点击: 509 [ 评论 ]
-
-
针对昨天的试验结果,书写了一个HTTP方式单线程下载的小程序,目前尚不支持断点续传。
希望各位看客使劲拍砖~~
原理:套接字发送HTTP GET方式的请求,然后根据HTTP响应,循环接收信息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <windows.h>
#include "HttpDown.h"
#pragma comment(lib, "Ws2_32.lib")
/************************************************************************/
/* 全局变量 */
/************************************************************************/
unsigned long g_FiltTotleLen = 0; //下载文件总大小
unsigned long g_DownedFileLen = 0; //已下载文件大小
/************************************************************************/
/* 函数描述:判断某一指定字符串是否为ip地址 */
/* 返回值: 是则返回0,否则返回-1 */
/* 作者: liuwp */
/* 创建时间:2008-03-27 */
/* 其他 */
/************************************************************************/
int IsIpAddrV4Str(char *pstr)
{
char a[4],b[4],c[4],d[4];
int num =0;
if ( !pstr)
return -1;
num = sscanf(pstr,"%3[0-9].%3[0-9].%3[0-9].%3[0-9]",a,b,c,d);
if ( num != 4 )
return -1;
if ( atoi(a)>255 || atoi(b)>255 || atoi(c) > 255 || atoi(d) >255 )
return -1;
return 0;
}
/************************************************************************/
/* 函数描述:根据下载地址获得HOST、IP等信息 */
/* 返回值: 成功返回0,失败返回-1 */
/* 作者: liuwp */
/* 创建时间:2008-03-27 */
/* 其他 */
/************************************************************************/
int GetDownInfoByUlr(const char *pDownUrl, char *pIpAddr, int *Port
, char *pGetInfo, int GetLen, char *RetErrorBuf, int ErrorLen)
{
int RetCode = 0;
char pUrlTempBuf[1000];
char pHostInf[500];
memset(pUrlTempBuf, 0, sizeof(pUrlTempBuf));
memset(pHostInf, 0, sizeof(pHostInf));
//参数判断
if (!pDownUrl || !pIpAddr || !Port || !pGetInfo || !RetErrorBuf)
return -1;
//拷贝字符串
strncpy(pUrlTempBuf, pDownUrl, sizeof(pUrlTempBuf)-1);
//获得HOST起始位置
char *pHostStart = strstr(pUrlTempBuf, "://");
if (!pHostStart)
{
_snprintf(RetErrorBuf, ErrorLen-1, "给定的下载URL格式错误,应该类似 http://HOST/GET ");
return -1;
}
//根据第一个 “/”获得HOST信息
pHostStart = pHostStart + strlen("://");
char *pHostEnd = strchr(pHostStart, ''/'');
if (!pHostEnd)
{
_snprintf(RetErrorBuf, ErrorLen-1, "给定的下载URL格式错误,应该类似 http://HOST/GET ");
return -1;
}
*pHostEnd = ''\0'';
_snprintf(pHostInf, sizeof(pHostInf)-1, "%s", pHostStart);
//获得GET信息
_snprintf(pGetInfo, GetLen-1, "/%s", pHostEnd+1);
//根据HOST信息获得对应的IP和端口
char *pPortStart = strchr(pHostInf, '':'');
if (!pPortStart)
{
*Port = 80; //默认为80
}
else
{
*pPortStart = ''\0'';
sscanf(pPortStart+1, "%d", Port);
}
//判断获得地址是否为IP,不是则转换为IP
RetCode = IsIpAddrV4Str(pHostInf);
if (RetCode != 0)
{
//初始化环境
WSADATA WSAData;
WSAStartup(MAKEWORD(2,2),&WSAData);
struct hostent* lpHostEnt = gethostbyname(pHostInf);
if(lpHostEnt == NULL)
{
_snprintf(RetErrorBuf, ErrorLen-1, "获得域名对应的IP地址失败, GetLastError()=%d", GetLastError());
return -1;
}
else
{
LPSTR lpaddr = lpHostEnt->h_addr_list[0];
if(lpaddr)
{
struct in_addr inAddr;
memcpy (&inAddr, lpaddr, 4);
sprintf(pIpAddr, "%s", inet_ntoa (inAddr) );
}
}
//清除环境
WSACleanup();
}
else
{
sprintf(pIpAddr, "%s", pHostInf );
}
return 0;
}
/************************************************************************/
/* 函数描述:通过HTTP协议进行下载 */
/* 返回值: 下载成功返回0,失败返回-1 */
/* 作者: liuwp */
/* 创建时间:2008-03-27 */
/* 其他 */
/************************************************************************/
int DownByHttpFun(const char *pDownUrl, const char *pDownFile, unsigned long *pTotleLen
, char *RetErrorBuf, int ErrorLen)
{
WSADATA WSAData;
sockaddr_in ServerAddr;
fd_set fdset;
struct timeval tv;
HANDLE hFileHandle = NULL;
bool FirstFlag = true;
int RetCode = 0;
int WriteLen = 0;
char SendBuf[1000];
char RecvBuf[4096];
char IpAddr[30];
char GetInfo[600];
int Port = 0;
char *pConLen = NULL;
DWORD dwFileSize = 0;
memset(IpAddr, 0, sizeof(IpAddr));
memset(GetInfo, 0, sizeof(GetInfo));
memset(RecvBuf, 0, sizeof(RecvBuf));
memset(SendBuf, 0, sizeof(SendBuf));
if (!pDownUrl || !pDownFile || !pTotleLen || !RetErrorBuf)
{
_snprintf(RetErrorBuf, ErrorLen-1, "给定参数不正确");
return -1;
}
//根据下载地址获得HOST, GET, IP,端口等信息
RetCode = GetDownInfoByUlr(pDownUrl, IpAddr, &Port
, GetInfo, sizeof(GetInfo)-1, RetErrorBuf, ErrorLen);
if (RetCode != 0)
return -1;
//初始化环境
WSAStartup(MAKEWORD(2,2),&WSAData);
//创建套接字
SOCKET Sock = socket(AF_INET, SOCK_STREAM, 0);
if ( SOCKET_ERROR == Sock )
{
_snprintf(RetErrorBuf, ErrorLen-1, "创建套接字失败, GetLastError()= %d !", GetLastError());
goto LEXIT;
}
//给定服务器地址
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr(IpAddr);
ServerAddr.sin_port = htons(Port);
//连接服务器
RetCode = connect(Sock, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr));
if (RetCode == SOCKET_ERROR)
{
_snprintf(RetErrorBuf, ErrorLen-1, "连接服务器失败, GetLastError()= %d !", GetLastError());
goto LEXIT;
}
//发送信息
_snprintf(SendBuf, sizeof(SendBuf)-1
, "GET %s HTTP/1.1\r\nHost: %s:%d\r\n\r\n"
,GetInfo, IpAddr, Port);
RetCode = send(Sock, SendBuf, strlen(SendBuf), 0);
if (RetCode < 0)
{
_snprintf(RetErrorBuf, ErrorLen-1, "发送数据失败, GetLastError()= %d !", GetLastError());
closesocket(Sock);
WSACleanup();
goto LEXIT;
}
//接收信息
FD_ZERO(&fdset);
FD_SET(Sock, &fdset);
tv.tv_sec = 2*60; //秒
tv.tv_usec = 0;
//打开文件
hFileHandle = CreateFile(pDownFile, GENERIC_WRITE, 0
, (LPSECURITY_ATTRIBUTES) NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,(HANDLE) NULL);
if (hFileHandle == INVALID_HANDLE_VALUE)
{
_snprintf(RetErrorBuf, ErrorLen-1, "打开文件%s失败, GetLastError()= %d !"
, pDownFile, GetLastError());
goto LEXIT;
}
//置空全局变量
g_FiltTotleLen = 0; //下载文件总大小
g_DownedFileLen = 0; //已下载文件大小
while(1)
{
memset(RecvBuf, 0, sizeof(RecvBuf));
int len = select(0, &fdset, NULL, NULL, &tv);
if (len < 1)
{
break;
}
//套接字接收信息
len = recv(Sock, RecvBuf, sizeof(RecvBuf)-1 ,0);
if (len <= 0)
{
break;
}
if (!FirstFlag) //不是第一次,追加写
{
dwFileSize = GetFileSize(hFileHandle, NULL);
SetFilePointer(hFileHandle, dwFileSize, NULL, FILE_BEGIN);
RetCode = WriteFile(hFileHandle, RecvBuf, len , (DWORD*)&WriteLen, NULL);
if (RetCode == 0)
{
_snprintf(RetErrorBuf, ErrorLen-1, "写入文件失败 ! GetLastError()= %d !", GetLastError());
goto LEXIT;
}
//获得此时已经下载的文件大小
g_DownedFileLen = dwFileSize + len;
//如果已经下载完毕,则跳出
if (g_DownedFileLen == *pTotleLen)
break;
continue;
}
//判断返回值
int RetCode = 0;
sscanf(RecvBuf+strlen("HTTP/1.1 "), "%d", &RetCode);
if (RetCode < 200 || RetCode > 300)
{
_snprintf(RetErrorBuf, ErrorLen-1, "下载失败, 服务器返回码为%d !", RetCode);
goto LEXIT;
}
//获得总大小
pConLen = strstr(RecvBuf, "\r\nContent-Length:");
if (!pConLen)
{
_snprintf(RetErrorBuf, ErrorLen-1, "HTTP协议中未给定文件的大小!");
goto LEXIT;
}
sscanf(pConLen, "\r\nContent-Length: %ld\r\n", pTotleLen);
g_FiltTotleLen = *pTotleLen;
char *pEnd = strstr(RecvBuf, "\r\n\r\n");
if (!pEnd)
{
break;
}
pEnd = pEnd + strlen("\r\n\r\n");
//移动到文件尾部,写文件
dwFileSize = GetFileSize(hFileHandle, NULL);
SetFilePointer(hFileHandle, dwFileSize, NULL, FILE_BEGIN);
RetCode = WriteFile(hFileHandle, pEnd, len-(pEnd-RecvBuf) , (DWORD*)&WriteLen, NULL);
if (RetCode == 0)
{
_snprintf(RetErrorBuf, ErrorLen-1, "写入文件失败 ! GetLastError()= %d !", GetLastError());
goto LEXIT;
}
//获得此时已经下载的文件大小
g_DownedFileLen = dwFileSize + len - (pEnd - RecvBuf);
//更改第一次接收标识
FirstFlag = false;
}
//下载完毕,判断下载大小是否相同
dwFileSize = GetFileSize(hFileHandle, NULL);
if (pTotleLen && *pTotleLen != 0 && *pTotleLen != dwFileSize)
{
_snprintf(RetErrorBuf, ErrorLen-1, "下载文件不完整, 请重新下载!");
goto LEXIT;
}
//关闭文件
if (hFileHandle)
{
CloseHandle(hFileHandle);
hFileHandle = NULL;
}
//关闭套接字
if (Sock != 0)
{
closesocket(Sock);
Sock = 0;
}
//清除环境
WSACleanup();
return 0;
LEXIT:
//关闭文件
if (hFileHandle)
{
CloseHandle(hFileHandle);
hFileHandle = NULL;
}
//关闭套接字
if (Sock != 0)
{
closesocket(Sock);
Sock = 0;
}
//清除环境
WSACleanup();
return -1;
}
/************************************************************************/
/* 函数描述:获得文件下载的相关信息 */
/* 返回值: */
/* 作者: liuwp */
/* 创建时间:2008-03-28 */
/* 其他 */
/************************************************************************/
float GetDownloadRate(unsigned long *pTotleLen , unsigned long *pDownLen)
{
float DownRate = 0;
if (!pTotleLen || !pTotleLen )
return 0;
//获得文件总大小和已下载的大小
*pTotleLen = g_FiltTotleLen;
*pDownLen = g_DownedFileLen;
//计算速率
if (g_FiltTotleLen == 0)
return 0;
if (g_DownedFileLen == 0)
return 0;
DownRate = (float)g_DownedFileLen/(float)g_FiltTotleLen;
return DownRate;
}
文章出处:飞诺网(www.diybl.com):http://www.diybl.com/course/3_program/c++/cppjs/2008331/107753.html