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
?
?
?
轉載于:https://www.cnblogs.com/Ray-chen/archive/2011/12/14/2287959.html
總結
以上是生活随笔為你收集整理的http 断点续传,Windows下HTTP方式单线程下载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: code blocks代码性能分析_记一
- 下一篇: Oracle为JDK 8寻求社区参与