C语言 HTTP 下载文件

C语言 HTTP协议下载文件,实现断点续传,socket通讯,目前只支持ip和port方式连接,有兴趣的读者可完善域名方式。
      代码分为 http.c: 实现http协议下载文件 ,socket.c: 封装linux socket函数,移植时只需修改socket.c中的函数即可。
     希望对大家有帮助,本人亲测可用!
http.c

点击(此处)折叠或打开

  1. //http.c
  2. //作者:王振
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <errno.h>
  6. #include <string.h>

  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <fcntl.h>

  10. #include "socket.h"
  11. #include "http.h"


  12. #define MAX_RECV_SIZE    1440//硬件单包最大的接收字节数
  13. char g_host[URL_LEN];
  14. char g_ip[URL_LEN+1];//ip/域名
  15. char g_port[5+1];

  16. char g_buf_send[4*1024];//发送数据暂存区
  17. char g_buf_recv[10*1024];//接收数据暂存区

  18. BreakPoint_ST g_break_point;

  19. /*
  20. 功能:判断断点有效性,现在校验url是否一致
  21. 参数:
  22. 返回:
  23. >0---------有效,已下载文件大小
  24. -1----------无效
  25. */
  26. int Get_Breakpoint_Available(BreakPoint_ST *breakpoint,char *url,char *file_crc)
  27. {
  28.     
  29.     //判断断点是否有效,后续加入文件校验码
  30.     if((memcmp(breakpoint->url,url,strlen(url))== 0)&&(breakpoint->recv_size== MAX_RECV_SIZE))
  31.         return breakpoint->download_size;
  32.     else
  33.     {
  34.         return -1;
  35.     }
  36.     
  37. }


  38. /*
  39. 功能:判断要下载文件是否存在断点
  40. 参数:
  41. filename---要下载的文件名
  42. file_crc----服务器返回下载文件的校验码
  43. 返回:
  44. 0---------无断点
  45. >0--------有断点,已下载文件大小
  46. */
  47. int Get_Breakpoint(char *url,char *filename,char *file_crc)
  48. {
  49.     char filename_bp[64];
  50.     int fd = -1;
  51.     int ret;
  52.     BreakPoint_ST break_point;
  53.     
  54.     //断点文件名 filename+bp
  55.     sprintf(filename_bp,"%s.bp",filename);

  56.     //检测是否存在filename断点文件
  57.     fd = open(filename_bp,O_RDONLY,S_IRUSR|S_IWUSR);
  58.     if(fd == -1)
  59.     {    
  60.         #ifdef DEBUG_HTTP
  61.         printf("no exsit %s\n",filename_bp);
  62.         #endif
  63.         return 0;
  64.     }

  65.     //存在断点
  66.     ret = read(fd,&break_point,sizeof(break_point));
  67.     if(ret != sizeof(break_point))
  68.     {
  69.         perror("ERR:Get_Breakpoint read");
  70.         exit(-1);
  71.     }

  72.     close(fd);

  73.     //判断断点是否有效
  74.     ret = Get_Breakpoint_Available(&break_point,url,file_crc);
  75.     if(ret > 0)
  76.         return ret;
  77.     else
  78.     {
  79.         
  80.         printf("%s not available\n",filename_bp);
  81.         remove(filename);
  82.         remove(filename_bp);
  83.         return 0;
  84.         
  85.     }
  86. }

  87. /*
  88. 功能:保存断点信息,文件名filename.bp
  89. 参数:
  90. filename---要下载的文件名
  91. file_crc----服务器返回下载文件的校验码
  92. 返回:
  93. 0---------成功
  94. >0--------有断点,已下载文件大小
  95. */
  96. int Save_Breakpoint(char *url,char *filename,int download_size,char *file_crc)
  97. {
  98.     int fd;
  99.     BreakPoint_ST breakpoint;
  100.     char filename_bp[128];//断点信息文件名,包含路径

  101.     sprintf(filename_bp,"%s.bp",filename);
  102.     /* 创建目的文件 */
  103.     if((fd=open(filename_bp,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)
  104.     {
  105.         fprintf(stderr,"Open %s Error:%s\n",filename_bp,strerror(errno));
  106.         exit(1);
  107.     }
  108.     memset(&breakpoint,0x0,sizeof(breakpoint));
  109.     strcpy(breakpoint.url,url);
  110.     //strcpy(breakpoint.crc,file_crc);
  111.     strcpy(breakpoint.filename,filename);
  112.     breakpoint.download_size = download_size;
  113.     breakpoint.recv_size= MAX_RECV_SIZE;
  114.     
  115.     //xu tioa zheng wei fen ci xie ru
  116.     if(write(fd,&breakpoint,sizeof(breakpoint)) != sizeof(breakpoint))
  117.     {
  118.         perror("ERR:Save_Breakpoint");
  119.         exit(1);
  120.     }

  121.     close(fd);

  122.     return 0;



  123. }

  124. /*
  125. 功能:保存文件,追加写
  126. 参数:
  127. 返回:
  128. 0---------成功
  129. */

  130. int Save_File(char *filebuf,int filelength,char *filename)
  131. {
  132.     int fd;
  133.     /* 创建目的文件追加写 */
  134.     if((fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR))==-1)
  135.     {
  136.         fprintf(stderr,"Open %s Error:%s\n",filename,strerror(errno));
  137.         exit(1);
  138.     }
  139.     //xu tioa zheng wei fen ci xie ru
  140.     if(write(fd,filebuf,filelength) != filelength)
  141.     {
  142.         perror("ERR:Save_File");
  143.         exit(1);
  144.     }

  145.     close(fd);

  146.     return 0;


  147. }

  148.  
  149. int HTTP_GetResponseCode(void)
  150. {
  151.  
  152.  
  153. }

  154.  /*
  155. 功能:读取http返回的协议实体主体长度
  156. 参数:
  157. revbuf--------接收到的返回值
  158. 返回值:
  159. >=0---------内容(实体主体)的长度
  160. -1-----------数据返回错误
  161. */
  162. int HTTP_GetRecvLength(char *revbuf)
  163. {
  164.     char *p1 = NULL;
  165.     int HTTP_Body = 0;//内容体长度
  166.     int HTTP_Head = 0;//HTTP 协议头长度


  167.     HTTP_Body = HTTP_GetContentLength(revbuf);
  168.     if(HTTP_Body == -1)
  169.         return -1;

  170.     p1=strstr(revbuf,"\r\n\r\n");
  171.     if(p1==NULL)
  172.         return -1;
  173.     else
  174.     {
  175.         HTTP_Head = p1- revbuf +4;// 4是\r\n\r\n的长度
  176.         return HTTP_Body+HTTP_Head;
  177.     }


  178. }


  179. /*
  180. 功能:读取http返回的Content-Length长度
  181. 参数:
  182. revbuf--------接收到的数据
  183. 返回值:
  184. >=0---------Content-Length长度
  185. -1-----------数据返回错误
  186. */
  187. int HTTP_GetContentLength(char *revbuf)
  188. {
  189.     char *p1 = NULL, *p2 = NULL;
  190.     int HTTP_Body = 0;//内容体长度

  191.     p1 = strstr(revbuf,"Content-Length");
  192.     if(p1 == NULL)
  193.         return -1;
  194.     else
  195.     {
  196.         p2 = p1+strlen("Content-Length")+ 2; 
  197.         HTTP_Body = atoi(p2);
  198.         return HTTP_Body;
  199.     }

  200. }

  201.  /*
  202.  功能:
  203.  参数:
  204.  sockfd--------接收到的返回值
  205.  返回值:
  206.  >0---------接收到长度
  207.  -1----------失败
  208.  =0---------服务端断开连接
  209.  注:内部接收缓冲10k
  210.  */

  211. int HTTP_Recv(int sockfd,char *buf_recv)
  212. {
  213.     int ret;
  214.     int recvlen=0;
  215.     int downloadlen = 0;
  216.     //int contentlen=0;
  217.     char buf_recv_tmp[10*1024+1];
  218.     
  219.     memset(buf_recv_tmp,0x0,sizeof(buf_recv_tmp)); 
  220.     while(1)
  221.     {
  222.         ret = Recv(sockfd,buf_recv_tmp+recvlen,sizeof(buf_recv_tmp)-1,0);

  223.         if(ret <= 0)//下载失败
  224.         {
  225.             perror("ERR:recv fail");
  226.             return ret;
  227.         }
  228.     
  229.     
  230.         if(recvlen == 0)
  231.         {
  232.             #ifdef DEBUG_HTTP_RECV
  233.             printf("recv len = %d\n", ret);
  234.              printf("recv = %s\n", buf_recv_tmp);
  235.             #endif
  236.             //获取需要下载长度;
  237.             downloadlen = HTTP_GetRecvLength(buf_recv_tmp);


  238.             #ifdef DEBUG_HTTP_RECV
  239.             printf("downloadlen = %d\n",downloadlen);
  240.             #endif
  241.         }

  242.         recvlen += ret;
  243.         #ifdef DEBUG_HTTP_RECV
  244.         printf("total recvlen = %d\n",recvlen);
  245.         #endif

  246.         if(downloadlen == recvlen)//下载完成
  247.             break;


  248.     }
  249.     memcpy(buf_recv,buf_recv_tmp,downloadlen);
  250.     return recvlen;

  251. }

  252. /*
  253. 功能:获取下载url中的文件名,最后一个/后的字符
  254. 参数:
  255. 返回值:
  256. 0-----------成功
  257. -1----------失败
  258. :内部接收缓冲10k
  259. */

  260. int HTTP_GetFileName(char *url,char *filename)
  261. {
  262.     //提取url中最后一个/后的内容
  263.     int len;
  264.     int i;

  265.     len = strlen(url);
  266.     for(i=len-1;i>0;i--)
  267.     {
  268.         if(url[i] == '/')
  269.             break;
  270.     }
  271.     if(== 0)//下载地址错误
  272.     {
  273.         printf("url not contain '/'\n");
  274.         return -1;
  275.     }
  276.     else
  277.     {
  278.     
  279.         strcpy(filename,url+i+1);
  280.         #ifdef DEBUG_HTTP
  281.         printf("filename=%s\n",filename);
  282.         #endif
  283.         return 0;
  284.     }
  285. }

  286. /*
  287. 功能:获取下载url中的路径,第一个/后的字符
  288. 参数:
  289. 返回值:
  290. 0-----------成功
  291. -1----------失败
  292. :url ex "http://host:port/path"
  293. */
  294. int HTTP_GetPath(char *url,char *path)
  295. {
  296.     char *p;

  297.     p = strstr(url,"http://");
  298.     if(== NULL)
  299.     {
  300.         p = strchr(url,'/');
  301.         if(== NULL)
  302.             return -1;
  303.         else
  304.         {
  305.             strcpy(path,p);
  306.             return 0;
  307.         }
  308.     }
  309.     else
  310.     {
  311.         p = strchr(url+strlen("http://"),'/');
  312.         if(== NULL)
  313.             return -1;
  314.         else
  315.         {
  316.             strcpy(path,p);
  317.             return 0;
  318.         }
  319.     }

  320. }
  321. /*
  322. 功能:获取下载url中的ip和port,ip支持域名,端口默认为80
  323. 参数:
  324. 返回值:
  325. 1-----------域名式
  326. 2-----------ip port式
  327. -1----------失败
  328. :url ex "http://host:port/path"
  329. */

  330. int HTTP_Get_IP_PORT(char *url,char *ip,char *port)
  331. {
  332.     char *= NULL;
  333.     int offset = 0;
  334.     char DOMAIN_NAME[128];

  335.     p = strstr(url,"http://");
  336.     if(== NULL)
  337.     {
  338.         offset = 0;
  339.     }
  340.     else
  341.     {
  342.         offset = strlen("http://");
  343.     }

  344.     p = strchr(url+offset,'/');
  345.     if(== NULL)
  346.     {
  347.         printf("url:%s format error\n",url);
  348.         return -1;
  349.         
  350.     }
  351.     else
  352.     {

  353.         memset(DOMAIN_NAME,0x0,sizeof(DOMAIN_NAME));
  354.         memcpy(DOMAIN_NAME,url+offset,(p-url-offset));
  355.         p = strchr(DOMAIN_NAME,':');
  356.         if(== NULL)
  357.         {
  358.             strcpy(ip,DOMAIN_NAME);
  359.             strcpy(port,"80");
  360.             //printf("ip %p,port %p\n",ip,port);
  361.             
  362.             #ifdef DEBUG_HTTP
  363.             printf("ip=%s,port=%s\n",ip,port);//debug info
  364.             #endif
  365.             return 1;

  366.         }
  367.         else
  368.         {    
  369.             *= '\0';

  370.             strcpy(ip,DOMAIN_NAME);
  371.             strcpy(port,p+1);
  372.             
  373.             #ifdef DEBUG_HTTP
  374.             printf("ip=%s,port=%s\n",ip,port);//debug info
  375.             #endif
  376.             return 2;

  377.         }


  378.         return 0;
  379.     }
  380.     
  381. }
  382. void Package_Url_Get_File(char *path, char *range)
  383. {
  384.     char buf[64];
  385.     memset(g_buf_send,0x0,sizeof(g_buf_send));         
  386.     sprintf(g_buf_send, "GET %s",path);

  387.     
  388.     //HTTP/1.1\r\n 前面需要一个空格
  389.     strcat(g_buf_send," HTTP/1.1\r\n");
  390.     strcat(g_buf_send, "Host: ");
  391.     strcat(g_buf_send, g_host);
  392.     //strcat(g_buf_send, ":");
  393.     //strcat(g_buf_send, PORT);
  394.     
  395.     sprintf(buf, "\r\nRange: bytes=%s",range);
  396.     strcat(g_buf_send,buf);
  397.     strcat(g_buf_send, "\r\nKeep-Alive: 200");
  398.     strcat(g_buf_send,"\r\nConnection: Keep-Alive\r\n\r\n");
  399.     

  400. }

  401. int Package_Url_Get_FileSize(char *url)
  402. {
  403.     
  404.     memset(g_buf_send,0x0,sizeof(g_buf_send));         
  405.     sprintf(g_buf_send, "HEAD %s",url);

  406.         //HTTP/1.1\r\n 前面需要一个空格
  407.     strcat(g_buf_send," HTTP/1.1\r\n");
  408.     strcat(g_buf_send, "Host: ");
  409.     strcat(g_buf_send, g_host);
  410.     //strcat(g_buf_send, ":");
  411.     //strcat(g_buf_send, PORT);
  412.     strcat(g_buf_send,"\r\nConnection: Keep-Alive\r\n\r\n");

  413.     return 0;
  414. }


  415. int HTTP_GetFileSize(int sockfd,char *path)
  416. {
  417.     int ret = -1;
  418.     char buf_recv_tmp[10*1024+1];

  419.     Package_Url_Get_FileSize(path);
  420. #ifdef DEBUG_HTTP
  421.     printf("send = %s \n",g_buf_send);
  422. #endif

  423.     Send(sockfd, g_buf_send, strlen(g_buf_send), 0);

  424.     memset(buf_recv_tmp,0x0,sizeof(buf_recv_tmp));                                                 
  425.     ret = Recv(sockfd,buf_recv_tmp,sizeof(buf_recv_tmp)-1,0);
  426. #ifdef DEBUG_HTTP
  427.     printf("recv len = %d\n", ret);
  428.     printf("recv = %s\n", buf_recv_tmp);
  429. #endif
  430.     if(ret <= 0)
  431.     {
  432.         perror("ERR:recv fail GetFileSize()");
  433.         return -1;

  434.     }
  435.     ret = HTTP_GetContentLength(buf_recv_tmp);
  436.     if(ret <= 0)
  437.         return -1;
  438.     else
  439.         return ret;


  440. }




  441. /*
  442. 功能:分段下载文件
  443. 参数:
  444. 返回值:
  445. >0----------已下载文件大小(不包含上次下载)
  446. -1----------失败
  447. */
  448. int HTTP_GetFile(int sockfd,char *path,int filelength,int download_size,char *filebuf)
  449. {
  450.     int count;
  451.     char range[32];
  452.     int i;
  453.     int j = 0;//成功下载次数
  454.     int ret = -1;
  455.     char *= NULL;
  456.     int download_index;//下载开始索引

  457.     count = (filelength%MAX_RECV_SIZE)?(filelength/MAX_RECV_SIZE +1):(filelength/MAX_RECV_SIZE);

  458.     download_index = download_size/MAX_RECV_SIZE;

  459.     for(i=download_index;i<count;i++)
  460.     {
  461.         //if(== 20)//测试断点
  462.             //break;


  463.         if((== (count-1))&&(filelength%MAX_RECV_SIZE))
  464.             sprintf(range,"%d-%d",i*MAX_RECV_SIZE,filelength-1);
  465.         else
  466.             sprintf(range,"%d-%d",i*MAX_RECV_SIZE,(i+1)*MAX_RECV_SIZE-1);


  467.         Package_Url_Get_File(path,range);
  468.         #ifdef DEBUG_HTTP
  469.          printf("send = %s \n",g_buf_send);
  470.         #endif
  471.          Send(sockfd, g_buf_send, strlen(g_buf_send), 0);

  472.         /*需改为提取http 返回协议头和协议体总长,然后定长接收*/
  473.         memset(g_buf_recv,0x0,sizeof(g_buf_recv));                                              
  474.         ret = HTTP_Recv(sockfd,g_buf_recv);
  475.         if(ret < 0)
  476.             break;
  477.         if(ret == 0 )//服务端断开连接
  478.         {
  479.             sockfd = Socket_Connect(g_ip,g_port);
  480.              i--;
  481.             continue;
  482.         }
  483.         /*提取协议体数据,保存在filebuf中*/
  484.         p = strstr(g_buf_recv,"\r\n\r\n");
  485.         if(== NULL)//jia ru duan dian baocun
  486.         {
  487.             printf("ERR:g_buf_recv not contain end flag\n");
  488.             break;
  489.         }
  490.          else
  491.          {
  492.              memcpy(filebuf+j*MAX_RECV_SIZE,p+4,MAX_RECV_SIZE);
  493.              j++;

  494.          }
  495.     }
  496.     if(== count)
  497.         return (filelength-download_size);
  498.     else
  499.         return (i*MAX_RECV_SIZE-download_size);
  500. }

  501. /*
  502. 功能:HTTP下载文件
  503. 参数:
  504. 返回值:
  505. 0----------下载完成
  506. -1---------失败
  507. -2---------部分下载完成
  508. :保存文件到bin所在目录
  509. */
  510. int HTTP_DownloadFile(char *url,char *save_path)
  511. {
  512.     int ret;
  513.     int sockfd;
  514.     int filesize;
  515.     int download_size;
  516.     char filename[FILENAME_LEN+1];
  517.     char filename_bp[FILENAME_LEN+3+1];
  518.     char *filebuf;
  519.     char save_file_path[FILENAME_LEN+1];//保存下载文件的路径+文件名

  520.     char path[PATH_LEN+1];//url中的path

  521.     //提取ip和port或url(url 暂不实现,需要gethostbyname linux)
  522.     ret = HTTP_Get_IP_PORT(url,g_ip,g_port);
  523.     if(ret == -1)
  524.         return -1;
  525.     else
  526.     {
  527.         sprintf(g_host,"%s:%s",g_ip,g_port);
  528.     }
  529.     //提取下载文件名
  530.     ret = HTTP_GetFileName(url,filename);
  531.     if(ret == -1)
  532.         return -1;

  533.     ret = HTTP_GetPath(url,path);
  534.     if(ret == -1)
  535.         return -1;
  536.     //sleep(3);//debug info
  537.     //建立连接
  538.     sockfd = Socket_Connect(g_ip,g_port);

  539.     //获取下载文件总大小
  540.     filesize = HTTP_GetFileSize(sockfd,path);
  541.     if(filesize == -1)
  542.         return -1;
  543.     //#ifdef DEBUG_HTTP
  544.     printf("http need download size %d\n",filesize);
  545.     //#endif
  546.     //malloc分配存储文件空间
  547.     filebuf = (char *)malloc(filesize);
  548.     if(filebuf == NULL)
  549.     {
  550.         perror("malloc filebuf fail");
  551.         return -1;
  552.     }
  553.     else
  554.         memset(filebuf,0x0,filesize);

  555.     download_size = Get_Breakpoint(url,filename,NULL);
  556.     #ifdef DEBUG_HTTP
  557.     printf("breakpoint download_size=%d\n",download_size);//debug info
  558.     sleep(3);//debug info
  559.     #endif
  560.     //分段下载文件
  561.     ret = HTTP_GetFile(sockfd,path,filesize,download_size,filebuf);
  562.     Close(sockfd);
  563.     if(ret < 0)
  564.     {
  565.         free(filebuf);
  566.         return -1;
  567.     }
  568.     else
  569.     {

  570.         sprintf(save_file_path,"%s%s",save_path,filename);
  571.         
  572.         #ifdef DEBUG_HTTP
  573.         printf("save_path=%s\n",save_path);
  574.         printf("filename=%s\n",filename);
  575.         printf("save_file_path=%s\n",save_file_path);
  576.         printf("download_size = %d\n",ret);
  577.         #endif
  578.         Save_File(filebuf,ret,save_file_path);
  579.         free(filebuf);
  580.         if((ret+download_size) == filesize)//全部下载完成
  581.         {
  582.             sprintf(filename_bp,"%s.bp",filename);
  583.             remove(filename_bp);
  584.             
  585.      printf("download success\n");
  586.             return 0;
  587.         }
  588.         else//部分下载完成
  589.         {
  590.             printf("part download success\n");
  591.             //保存断点信息
  592.             Save_Breakpoint(url,save_file_path,ret+download_size,NULL);
  593.             return -2;
  594.         }
  595.     }

  596. }
socket . c  

点击(此处)折叠或打开

  1. //socket.c
  2. //作者:王振

  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <errno.h>
  6. #include <string.h>
  7. #include <sys/types.h>
  8. #include <netinet/in.h>
  9. #include <sys/socket.h>
  10. #include <sys/wait.h>

  11. #include "socket.h"

  12. int Connect(int fd, struct sockaddr *addr, socklen_t len)
  13. {
  14.     int result;
  15.     if ((result = connect(fd, addr, len)) == -1) {
  16.         perror("connect");
  17.         exit(EXIT_FAILURE);
  18.     }
  19.     return 0;
  20. }

  21. int Socket_Connect(char *ip,char *port)
  22. {
  23.     struct sockaddr_in addr;

  24.     int sockfd;
  25.     int len;

  26.     
  27.     addr.sin_family = AF_INET;
  28.     addr.sin_addr.s_addr = inet_addr(ip);//127.0.0.1为本机ip
  29.     addr.sin_port = htons(atoi(port));
  30.     len = sizeof(addr);
  31.         
  32.     sockfd = socket(AF_INET, SOCK_STREAM, 0);
  33.     Connect(sockfd, (struct sockaddr*)&addr, len);
  34.     return sockfd;

  35. }


  36. /*
  37. 功能:向socketfd发送数据,内部实现了循环发送len长度
  38. 参数:
  39. sockfd 是代表你与远程程序连接的套接字描述符。
  40. msg 是一个指针,指向你想发送的信息的地址。
  41. len 是你想发送信息的长度。
  42. flags 发送标记。一般都设为0
  43. 返回:
  44. 0-------- 成功
  45. 退出---失败
  46. 修改:
  47. 备注:
  48. 王振
  49. */
  50. int Send(int sockfd, char *sendbuf, int len, int flags)
  51. {
  52.     int sendlen = 0;
  53.     int ret = -1;

  54.     while(sendlen < len)
  55.     {
  56.         ret = send(sockfd, sendbuf+sendlen, len-sendlen, flags); 
  57.         if(-== ret)
  58.         {
  59.             perror("send");
  60.             exit(EXIT_FAILURE);
  61.         }
  62.         else
  63.             sendlen += ret;

  64.     }

  65.     return 0;

  66. }

  67. int Close(int sockfd)
  68. {
  69.     return close(sockfd);
  70.     

  71. }
  72. int Recv(int sockfd, char *recvbuf, int len, int flags)
  73. {
  74.     int recv_len;

  75.     if ((recv_len = recv(sockfd, recvbuf, len, flags)) < 0)
  76.     {
  77.         perror("recv error");
  78.         exit(EXIT_FAILURE);
  79.     }

  80.     return recv_len;
  81. }


很早就有这个想法了,知道今天才实现了一下,参考了周立发的《《C语言实现HTTP下载例子》》和林超旗整理的《《HTTP协议详解》》,特此谢谢!由于今天时间有限,所以大概的实现了一下(目前只在ubuntu环境下测试的),跨平台部分还需要完善,后面会陆续整理出完整的版本。代码如下:

头文件

/*
	$Id: httpdownload.h, v1.0.0, 2011.6.28, YellowBug $
	$@@: http下载 
*/
#ifndef PENQ_HTTP_DOWNLOAD_H_
#define PENQ_HTTP_DOWNLOAD_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#if defined(__GNUC__)
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#define PATH_SPLIT			'/'
typedef int	HSOCKET;
#define closesocket(s)		close(s)
#elif defined(_WIN32)
#include <windows.h>
#include <winsock2.h>
#define PATH_SPLIT			'//'	
typedef SOCKET HSOCKET;
#endif

#define DEF_HTTP_PORT			80
#define HTTP_MAX_PATH			260
#define HTTP_MAX_REQUESTHEAD	1024
#define HTTP_MAX_RESPONSEHEAD	2048
#define HTTP_DOWN_PERSIZE		128	
#define HTTP_FLUSH_BLOCK		1024

/*
	$@@ 下载状态
	$@ HDL_URLERR: 提供的url非法
	$@ HDL_SERVERFAL: 根据host地址无法找到主机 
	$@ HDL_TIMEOUT: 连接下载地址超时
	$@ HDL_READYOK: 连接下载地址成功,准备下载
	$@ HDL_DOWNING: 正在下载
	$@ HDL_DISCONN: 断开连接了
	$@ HDL_FINISH: 下载完成
*/
enum 
{
	HDL_URLERR = 0xe0,
	HDL_SERVERFAL = 0xe1,
	HDL_SOCKFAL = 0xe2,
	HDL_SNDREQERR = 0xe3,
	HDL_SERVERERR = 0xe4,
	HDL_CRLOCFILEFAL = 0xe5,
	HDL_TIMEOUT = 0x100,
	HDL_NORESPONSE = 0x101,
	HDL_READYOK = 0x104,
	HDL_DOWNING = 0x105,
	HDL_DISCONN = 0x106,
	HDL_FINISH = 0x107
};

/*
	$@@ 下载回调函数,主要用于监听下载过程
	$@ 第一个参数: 下载标识符号(上面枚举中的值)
	$@ 第二个参数: 需要下载的总字节数
	$@ 第三个参数: 已经下载的总字节数
	$@ 第四个参数: 距离上一次下载的时间戳(毫秒)
	$@ 返回值: 下次下载的字节数(0: 表示默认值, 小于0则下载中断, 大于0则位下次下载指定的字节)
*/
typedef int (*HTTPDOWNLOAD_CALLBACK)(int, unsigned int, unsigned int, unsigned int);

/*
	$@@ 通过指定的url下载资源
	$@ url: 下载资源地址
	$@ filepath: 存储在本地的路径(如果为NULL则存储在当前目录)
	$@ filename: 存储的文件名(如果为NULL则位url中分析的文件名)
	$@ http_func: 下载过程监听函数(如果为NULL则不监听下载过程)
*/
void pq_http_download(const char *url, const char *filepath, const char *filename, HTTPDOWNLOAD_CALLBACK http_func);
#endif /* #ifndef PENQ_HTTP_DOWNLOAD_H_ */
/* END */

实现:
/*
	$Id: httpdownload.c, v1.0.0, 2011.6.28, YellowBug $
	$@@: http下载 
*/
#include "httpdownload.h"

#define HERROR(exp, err)	do {														 /
								if (exp) {												 /
									printf("(%s,%d) %s/n", __FILE__, __LINE__, #exp);	 /
									if(S_http_callback) S_http_callback((err), 0, 0, 0); /
									exit(1);                                             /
								}                                                        /
							} while(0)							

#define HCALLBACK(a, b, c, d)		(S_http_callback ? S_http_callback((a), (b), (c), (d)) : 0)
/*
	$@@ 文件域的局部变量
*/
static HTTPDOWNLOAD_CALLBACK 	S_http_callback = NULL;
static const char * S_request_head = "GET %s HTTP/1.1/r/n" /
							  		 "Accept: */*/r/n" /
							  		 "Accept-Language: zh-cn/r/n" /
							  		 "User-Agent: Molliza/4.0(compatible;MSIE6.0;windows NT 5.0)/r/n" /
							  		 "Host: %s/r/n" /
							  		 "Connection: close/r/n/r/n";

#if defined(__GNUC__)
unsigned int get_tick() 
{
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return (unsigned int)(tv.tv_sec*1000+tv.tv_usec/1000);
}
#elif defined(_WIN32)
#define get_tick()		GetTickCount()
#endif

/*
	$@@ 从指定长度(len0)的字符串的右端开始找到第一个位ch的字符
*/
static char * r_strchr(const char *p, int len, char ch)
{
	int last_pos = -1;
	int i = 0;
	while ( *(p+i) != '/0' ) {
		if ( *(p+i) == ch ) last_pos = i;
		if ( ++i >= len ) break;	
	}
	return last_pos >= 0 ? (char *)(p+last_pos) : NULL;
}

/*
 	$@@ 解析url,分析出web地址,端口号,下载的资源名称
*/
static void parse_url(const char *url, char *web, int *port, char *filepath, char *filename)
{
	int web_len = 0;
	int web_start = 0;
	int file_len = 0;
	int file_start = 0;
	int path_len = 0;
	int path_start = 0;
	char *pa = NULL;
	char *pb = NULL;
	if ( !strncmp(url, "http://", 7) ) web_start = 7;
	else if ( !strncmp(url, "https://", 8) ) web_start = 8;
	
	pa = strchr(url+web_start, '/');
	pb = strchr(url+web_start, ':');
	
	/* 获取web */	
	if ( pa ) {
		web_len = (int)(pa - (url+web_start));
	} else {
		if ( pb ) web_len = pb - (url+web_start);
		else web_len = strlen(url+web_start);		
	}
	HERROR(0 == web_len, HDL_URLERR);
	memcpy(web, url+web_start, web_len);
	web[web_len] = '/0';		
	/* 获取filename */
	if ( pb ) {
		pa = r_strchr(url+web_start, (int)(pb-(url+web_start)), '/');
		file_len = (int)(pb - pa - 1);
		file_start = (int)(pa + 1 - url);
		*port = atoi(pb+1);
	} else {
		pa = r_strchr(url+web_start, strlen(url+web_start), '/');
		HERROR(NULL == pa, HDL_URLERR);
		file_len = strlen(pa+1);
		file_start = (int)(pa + 1 - url);
		*port = DEF_HTTP_PORT;
	}
	HERROR(NULL == pa, HDL_URLERR);
	memcpy(filename, url+file_start, file_len);
	filename[file_len] = '/0';
	
	/* 获取filepath */
	path_start = web_start + web_len;
	path_len = file_start - path_start + file_len;
	memcpy(filepath, url+web_start+web_len, file_start - path_start + file_len);
	filepath[path_len] = '/0';
}

/*
	$@@ 封装request报头
*/
int package_request(char *request, const char *web, const char *filepath)
{
	int n = sprintf(request, S_request_head, filepath, web);
	request[n] = '/0';
	return n;
}
/*
	$@@ 发送请求报头
*/
int send_request(HSOCKET sock, const char *buf, int n)
{
	int already_bytes = 0;
	int per_bytes = 0;	
	
	while ( already_bytes < n ) {
		per_bytes = send(sock, buf+already_bytes, n-already_bytes, 0);
		if ( per_bytes < 0 ) {
			break;
		} else if ( 0 == per_bytes ) {
			HCALLBACK(HDL_DISCONN, 0, 0, 0);
			break;
		} else
			already_bytes += per_bytes;	
	}
	return already_bytes;
}

/*
	$@@ 获取响应报头(状态行和消息报头)
*/
int get_response_head(HSOCKET sock, char *response, int max)
{
	int total_bytes = 0;
	int per_bytes = 0;
	int over_flag = 0;
	char ch;
	
	do {		
		per_bytes = recv(sock, &ch, 1, 0);
		if ( per_bytes < 0 ) break;
		else if ( 0 == per_bytes ) {
			HCALLBACK(HDL_DISCONN, 0, 0, 0);
			break;
		} else {
			if ( '/r' == ch || '/n' == ch ) over_flag++;
			else over_flag = 0;
			
			response[total_bytes] = ch;			
			if ( total_bytes >= max ) break;
			else total_bytes += per_bytes;
		}
	} while ( over_flag < 4 );
	response[total_bytes] = '/0';
	return total_bytes;
}
void parse_response_head(char *response, int n, int *server_status, unsigned int *down_total)
{
	int i = 1;
	char *pstatus = strstr(response, "HTTP/1.1");
	char *plen = strstr(response, "Content-Length:");	
	printf("%s/n", response);
	
	if ( pstatus ) {
		pstatus += strlen("HTTP/1.1");
		i = 1;	
		while ( *(pstatus+i) == ' ' ) ++i;
		*server_status = atoi(pstatus+i);
	} else
		*server_status = 0;
	if ( plen ) {
		plen += strlen("Content-Length:");
		i = 1;
		while ( *(plen+i) == ' ' ) ++i;
		*down_total = (unsigned int)atoi(plen+i); 
	} else
		*down_total = 0;
	printf("status: %d/n", *server_status);
	printf("total: %u/n", *down_total);
}
/*
	$@@ 创建本地文件
*/
FILE * create_local_file(const char *local_path, const char *local_file, const char *remote_file)
{
	int len = strlen(local_path);
	static char filename[HTTP_MAX_PATH+1];
	if ( local_path[len-1] != PATH_SPLIT ) {
		if ( local_file ) len = sprintf(filename, "%s%c%s", local_path, PATH_SPLIT, local_file);	
		else len = sprintf(filename, "%s%c%s", local_path, PATH_SPLIT, remote_file);		
 	} else {
		if ( local_file ) len = sprintf(filename, "%s%s", local_path, local_file);	
		else len = sprintf(filename, "%s%s", local_path, remote_file);	
	}
	filename[len] = '/0'; 
	printf("ready to create local file: %s/n", filename);
	return fopen(filename, "wb+");
}
/*
	$@@ 开始下载
*/
void start_download(HSOCKET sock, unsigned int total_bytes, FILE *stream)
{
	unsigned int already_bytes;
	int per_bytes;
	int flush_bytes;
	unsigned int total_tim;
	unsigned int per_tim;
	static char buf[HTTP_DOWN_PERSIZE];
	
	do {
		per_tim = get_tick();
		per_bytes = recv(sock, buf, HTTP_DOWN_PERSIZE, 0);
		per_tim = get_tick() - per_tim;
		if ( per_bytes < 0 ) break;
		else if ( 0 == per_bytes ) {
			HCALLBACK(HDL_DISCONN, total_bytes, already_bytes, 0);
			break;		
		} else {
			fwrite(buf, 1, per_bytes, stream);
			already_bytes += (unsigned int)per_bytes;
			
			flush_bytes += per_bytes;
			if ( flush_bytes >= HTTP_FLUSH_BLOCK ) {
				fflush(stream);
				flush_bytes = 0;	
			}
			HCALLBACK(HDL_DOWNING, total_bytes, already_bytes, per_tim);
		}
	} while ( already_bytes < total_bytes );
	
	if ( flush_bytes > 0 )
		fflush(stream);

	HCALLBACK(HDL_FINISH, total_bytes, already_bytes, total_tim);
}

void pq_http_download(const char *url, const char *local_path, const char *local_file, HTTPDOWNLOAD_CALLBACK http_func)
{
	int port;
	char web[HTTP_MAX_PATH+1];
	char remote_path[HTTP_MAX_PATH+1];
	char remote_file[HTTP_MAX_PATH+1];
	char request[HTTP_MAX_REQUESTHEAD+1];
	char response_head[HTTP_MAX_RESPONSEHEAD+1];
	int request_len = 0;
	int response_len = 0;
	unsigned int down_total = 0;
	int server_status = 404;
	HSOCKET sock = -1;
	struct hostent *host = NULL;
	struct sockaddr_in server_addr;	
	FILE *stream = NULL;
	S_http_callback = http_func;
	parse_url(url, web, &port, remote_path, remote_file);
	request_len = package_request(request, web, remote_path);
	host = gethostbyname(web);
	HERROR(NULL==host, HDL_SERVERFAL);
	
	sock = socket(AF_INET, SOCK_STREAM, 0);
	HERROR(-1==sock, HDL_SOCKFAL);
	
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr = *((struct in_addr *)host->h_addr);
	HERROR(-1==connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)), HDL_TIMEOUT);
	HERROR(request_len != send_request(sock, request, request_len), HDL_SNDREQERR);
	response_len = get_response_head(sock, response_head, HTTP_MAX_RESPONSEHEAD);
	HERROR(response_len <= 0, HDL_NORESPONSE);
	parse_response_head(response_head, response_len, &server_status, &down_total);
	switch ( server_status ) {
		case 200:
			if ( down_total > 0 ) {
				stream = create_local_file(local_path, local_file, remote_file);
				HERROR(NULL == stream, HDL_CRLOCFILEFAL);
				HCALLBACK(HDL_READYOK, down_total, 0, 0);
				start_download(sock, down_total, stream);
				fclose(stream);		
			} else {
				HCALLBACK(HDL_FINISH, 0, 0, 0);
			}
		default:
			HCALLBACK(HDL_SERVERERR, 0, 0, 0);
	}
	closesocket(sock);
}

/* END */

测试代码:

#include <stdio.h>
#include "httpdownload.h"
int http_callback(int status, unsigned int total_bytes, unsigned int already_bytes, unsigned int tim)
{
	switch ( status ) {
		case HDL_READYOK:
			printf("ready to download, total.bytes=%u/n", total_bytes);
			break;
		case HDL_DOWNING:
			printf("downloading... (%u/%u) bytes/n", already_bytes, total_bytes);
			break;
		case HDL_FINISH:
			printf("download finish! download total.bytes=%u/n", already_bytes);
			break;
		default:
			printf("status: %#x/n", status);	
	}	
	return 0;
}

int main(int argc, char *argv[])
{
	pq_http_download(argv[1], argv[2], NULL, http_callback);
}




一个http下载程序的c实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>

#define RECVBUFSIZE    2048
#define READBUFSIZE     2048

/********************************************
功能:搜索字符串右边起的第一个匹配字符
********************************************/
char * Rstrchr(char * s, char x)  {
int i = strlen(s);
if(!(*s))  return 0;
while(s[i-1]) if(strchr(s + (i - 1), x))  return (s + (i - 1));  else i--;
return 0;
}

/********************************************
功能:把字符串转换为全小写
********************************************/
void ToLowerCase(char *s)  {
while(*s)  {
*s=tolower(*s);
s++;
}//while
}

/**************************************************************
功能:从字符串src中分析出网站地址和端口,并得到用户要下载的文件
***************************************************************/
void GetHost(char * src, char * web, char * file, int * port)  {
char * pA;
char * pB;
memset(web, 0, sizeof(web));
memset(file, 0, sizeof(file));
*port = 0;
if(!(*src))  return;
pA = src;
if(!strncmp(pA, "http://", strlen("http://")))  pA = src+strlen("http://");
else if(!strncmp(pA, "https://", strlen("https://")))  pA = src+strlen("https://");
pB = strchr(pA, '/');
if(pB)  {
memcpy(web, pA, strlen(pA) - strlen(pB));
if(pB+1)  {
memcpy(file, pB + 1, strlen(pB) - 1);
file[strlen(pB) - 1] = 0;
}
}
else  memcpy(web, pA, strlen(pA));
if(pB)  web[strlen(pA) - strlen(pB)] = 0;
else  web[strlen(pA)] = 0;
pA = strchr(web, ':');
if(pA)  *port = atoi(pA + 1);
else *port = 80;
}


int find_start(char *buf, int size) 
{
int i;
char *p;
p = strstr(buf, "\r\n\r\n");
if(p) {
i = p - buf + 4;
if(i >= size)
i = -1;
}
else
i = -1;
return i;
}

//唯一的参数就是网址网址
int main(int argc, char **argv) {
int sockfd;
int fd;
struct hostent *host;
int portnumber;
char url[128];
char host_addr[256];
char host_file[1024];
char request[1024];
struct sockaddr_in server_sockaddr;
char recvbuf[RECVBUFSIZE];
int sendbytes, recvbytes, totalsend, totalsended;
int file_start_pos;

sprintf(url, "%s", argv[1]);
ToLowerCase(url);
GetHost(url, host_addr, host_file, &portnumber);
host = gethostbyname(host_addr);

sockfd = socket(PF_INET, SOCK_STREAM, 0);
server_sockaddr.sin_family = PF_INET;
server_sockaddr.sin_port = htons(portnumber);
server_sockaddr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(server_sockaddr.sin_zero), 8);
connect(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr));

sprintf(request, "GET /%s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: zh-cn\r\n\
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n\
Host: %s:%d\r\nConnection: Keep-Alive\r\n\r\n", host_file, host_addr, portnumber);

sendbytes = 0;
totalsended = 0;            //already send
totalsend = strlen(request);        //will send
while(totalsended < totalsend) {    
sendbytes = send(sockfd, request+totalsended, totalsend - totalsended, 0);
totalsended += sendbytes;
}//while

fd = open("download", O_CREAT | O_TRUNC | O_RDWR, 0777);
recvbytes = recv(sockfd, recvbuf, RECVBUFSIZE, 0);    //delete the head
file_start_pos = find_start(recvbuf, recvbytes);
write(fd, recvbuf+file_start_pos, recvbytes-file_start_pos);
while((recvbytes = recv(sockfd, recvbuf, RECVBUFSIZE, 0)) > 0) {
write(fd, recvbuf, recvbytes);
}//while

close(fd);
close(sockfd);
return 0;
}//main()


  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值