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://www.mzph.cn/news/462337.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

给创业者的30条建议

http://www.cocoachina.com/programmer/20150206/11119.html 去年年底的时候&#xff0c;我&#xff08;Firstround Review 主编&#xff09;在 Facebook 公司的咖啡厅里和 Caryn Marooney 交流着创业公司应该注意些什么事情。Caryn Marooney 现在是 Facebook 公司科技交流部门…

php swoole websocket vue 实现聊天室案例

代码地址: https://github.com/9499574/demo_chat_room 转载于:https://www.cnblogs.com/phper8/p/11017892.html

数据结构 练习21-trie的原理分析和应用

前言 今天具体分析一下trie树&#xff0c;包括&#xff1a;原理分析&#xff0c;应用场合&#xff0c;复杂度分析&#xff0c;与hash的比较&#xff0c;源码展现。大部分内容来自互联网&#xff0c;文中会注明出处。 原理分析 主要是hash树的变种&#xff0c;先看下图&#xff…

在辞职后的旅途中:我写了个App 创立了一家公司

http://www.cocoachina.com/programmer/20150206/11119.html 英文原文&#xff1a;How I built a startup while traveling to 20 countries 一年前&#xff0c;我离开了旧金山&#xff0c;变卖或者送掉了一切我所拥有的东西&#xff0c;然后买了一只 40 升的登山包。 我旅行到…

Android找工作系列之自定义View

...转载于:https://www.cnblogs.com/hbolin/p/11019959.html

POJ 1088-滑雪

矩阵里的数字代表当前点的高度&#xff0c;只能从高的点滑到低的点&#xff0c;求最长能滑的距离。初始点 不规定。我们可以向每个点的四周搜索&#xff0c;能走则就在当前距离加1。并将已经求的值保存在 二维数组中。&#xff08;记忆化搜索&#xff09; /*Accepted 252K …

PostgreSQL的 initdb 源代码分析之二十一

继续分析&#xff1a; setup_schema(); 展开&#xff1a; 实质就是创建info_schema。 cmd 是&#xff1a; "/home/pgsql/project/bin/postgres" --single -F -O -c search_pathpg_catalog -c exit_on_errortrue -j template1 >/dev/null infor_schem_file是&…

Be My Eyes app:我是你的眼

http://www.cocoachina.com/industry/20150122/10979.html Be My Eyes是丹麦软件工作室Robocat为一家同名非营利性企业推出的一款应用&#xff0c;主要通过视频聊天的方式将志愿者和视力受损的患者联系起来&#xff0c;从而实现远程协助的功能。 Be My Eyes的核心概念非常简单-…

C#面试题整理(不带答案)

1.维护数据库的完整性、一致性、你喜欢用触发器还是自写业务逻辑&#xff1f;为什么? 2.什么是事务&#xff1f;什么是锁&#xff1f; 3.什么是索引&#xff0c;有什么优点&#xff1f; 4.视图是什么&#xff1f;游标是什么&#xff1f; 5.什么是存储过程&#xff1f;有什么优…

nRF905

nRF905[1]无线芯片是有挪威NORDIC公司出品的低于1GHz无线数传芯片&#xff0c;主要工作于433MHz、868MHz和915MHz的ISM频段。芯片内置频率合成器、功率放大器、晶体振荡器和调制器等功能模块&#xff0c;输出功率和通信频道可通过程序进行配置。非常适合于低功耗、低成本的系统…

用户界面概述

视图是提供了良好定义的功能集合的内容区域。 控件则是能够触发即时动作或可视化结果的图形对象 无论是什么类型的应用程序&#xff0c;都有一个应用程序窗口&#xff0c;该窗口为您提供了一个能够呈现应用程序的所有信息的背景。但是用户对这个窗口没有概念&#xff0c;他们对…

Firefox for iOS现身Github 使用Swift编写

http://www.cocoachina.com/industry/20141208/10545.html 自从Mozilla新CEO走马上任以来&#xff0c;该公司对于发展路线显然与以往有所不同&#xff0c;对该公司最重要的产品Firefox浏览器来说&#xff0c;也有了很多大的改变&#xff0c;包括前几天Mozilla宣布&#xff0c;它…

UVA 213 Message Decoding

题目链接&#xff1a;https://vjudge.net/problem/UVA-213 题目翻译摘自《算法禁赛入门经典》 题目大意 考虑下面的 01 串序列&#xff1a;  0, 00, 01, 10, 000, 001, 010, 011, 100, 101, 110, 0000, 0001, …, 1101, 1110, 00000, …  首先是长度为 1 的串&#xff0c;然…

The Event System

The Event System 在Qt中&#xff0c;事件是继承了虚拟类QEvent的对象&#xff0c;它代表了程序所发生的事情或者程序需要知道的一个外部活动的结果。事件可以被任意 QObject子类的实例接收和处理&#xff0c;是与widgets密切相关。本文描述了在一个典型的程序中事件是如何被传…

分组取最新记录的SQL

常遇到这样的情况&#xff0c;要取得所有客户的最新交易记录&#xff0c;读取网站所有浏览者最后一次访问时间。一个客户只读取最新的一次记录&#xff0c;相同&#xff0c;大部分的人首先想 到的就是排除所有记录&#xff0c;相同的只取一条。用distint,但是distint只能取到一…

g++参数介绍

地址:http://www.cnblogs.com/lidan/archive/2011/05/25/2239517.html [介绍] gcc and g分别是gnu的c & c编译器 gcc/g在执行编译工作的时候&#xff0c;总共需要4步 1.预处理,生成.i的文件 预处理器cpp 2.将预处理后的文件不转换成汇编语言,生成文件.s 编译器e…

利用CVE-2019-1040 - 结合RCE和Domain Admin的中继漏洞

0x00 前言 在本周之前&#xff0c;微软发布了针对CVE-2019-1040的补丁&#xff0c;这是一个允许绕过NTLM身份验证中继攻击的漏洞。这个漏洞是由Marina Simakov和Yaron Zinar&#xff08;以及微软咨询公司的其他几位成员&#xff09;发现的&#xff0c;他们在这里发表了一篇关于…

URL 学习总结

1、绝对路径&#xff08;以"/"斜线开头的路径&#xff0c;代表相对于当前Web应用&#xff09;&#xff1a; a)地址给服务器用&#xff0c;web应用名称可以省略。 请求包含&#xff1a;request.getRequestDispatcher("/index.jsp").include(request, r…

[转]DEV界面

DevExpress控件使用经验总结 DevExpress是一个比较有名的界面控件套件&#xff0c;提供了一系列的界面控件套件的DotNet界面控件。本文主要介绍我在使用DevExpress控件过程中&#xff0c;遇到或者发现的一些问题解决方案&#xff0c;或者也可以所示一些小的经验总结。总体来讲&…

g++和gcc的区别

编译阶段是相同的&#xff0c;链接阶段g默认链接c库&#xff0c;gcc没有。 所以一般情况下用gcc编译c文件&#xff0c;用g编译cpp文件。 但是也可以用gcc编译cpp文件&#xff0c;但后面需要加一个选项-lstdc&#xff0c;作用是链接c库 还可以用g编译c文件