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.  
  13. #define MAX_RECV_SIZE    1440//硬件单包最大的接收字节数
  14. char g_host[URL_LEN];
  15. char g_ip[URL_LEN+1];//ip/域名
  16. char g_port[5+1];
  17. char g_buf_send[4*1024];//发送数据暂存区
  18. char g_buf_recv[10*1024];//接收数据暂存区
  19. BreakPoint_ST g_break_point;
  20. /*
  21. 功能:判断断点有效性,现在校验url是否一致
  22. 参数:
  23. 返回:
  24. >0---------有效,已下载文件大小
  25. -1----------无效
  26. */
  27. int Get_Breakpoint_Available(BreakPoint_ST *breakpoint,char *url,char *file_crc)
  28. {
  29.     
  30.     //判断断点是否有效,后续加入文件校验码
  31.     if((memcmp(breakpoint->url,url,strlen(url))== 0)&&(breakpoint->recv_size== MAX_RECV_SIZE))
  32.         return breakpoint->download_size;
  33.     else
  34.     {
  35.         return -1;
  36.     }
  37.     
  38. }
  39. /*
  40. 功能:判断要下载文件是否存在断点
  41. 参数:
  42. filename---要下载的文件名
  43. file_crc----服务器返回下载文件的校验码
  44. 返回:
  45. 0---------无断点
  46. >0--------有断点,已下载文件大小
  47. */
  48. int Get_Breakpoint(char *url,char *filename,char *file_crc)
  49. {
  50.     char filename_bp[64];
  51.     int fd = -1;
  52.     int ret;
  53.     BreakPoint_ST break_point;
  54.     
  55.     //断点文件名 filename+bp
  56.     sprintf(filename_bp,"%s.bp",filename);
  57.     //检测是否存在filename断点文件
  58.     fd = open(filename_bp,O_RDONLY,S_IRUSR|S_IWUSR);
  59.     if(fd == -1)
  60.     {    
  61.         #ifdef DEBUG_HTTP
  62.         printf("no exsit %s\n",filename_bp);
  63.         #endif
  64.         return 0;
  65.     }
  66.     //存在断点
  67.     ret = read(fd,&break_point,sizeof(break_point));
  68.     if(ret != sizeof(break_point))
  69.     {
  70.         perror("ERR:Get_Breakpoint read");
  71.         exit(-1);
  72.     }
  73.     close(fd);
  74.     //判断断点是否有效
  75.     ret = Get_Breakpoint_Available(&break_point,url,file_crc);
  76.     if(ret > 0)
  77.         return ret;
  78.     else
  79.     {
  80.         
  81.         printf("%s not available\n",filename_bp);
  82.         remove(filename);
  83.         remove(filename_bp);
  84.         return 0;
  85.         
  86.     }
  87. }
  88. /*
  89. 功能:保存断点信息,文件名filename.bp
  90. 参数:
  91. filename---要下载的文件名
  92. file_crc----服务器返回下载文件的校验码
  93. 返回:
  94. 0---------成功
  95. >0--------有断点,已下载文件大小
  96. */
  97. int Save_Breakpoint(char *url,char *filename,int download_size,char *file_crc)
  98. {
  99.     int fd;
  100.     BreakPoint_ST breakpoint;
  101.     char filename_bp[128];//断点信息文件名,包含路径
  102.     sprintf(filename_bp,"%s.bp",filename);
  103.     /* 创建目的文件 */
  104.     if((fd=open(filename_bp,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)
  105.     {
  106.         fprintf(stderr,"Open %s Error:%s\n",filename_bp,strerror(errno));
  107.         exit(1);
  108.     }
  109.     memset(&breakpoint,0x0,sizeof(breakpoint));
  110.     strcpy(breakpoint.url,url);
  111.     //strcpy(breakpoint.crc,file_crc);
  112.     strcpy(breakpoint.filename,filename);
  113.     breakpoint.download_size = download_size;
  114.     breakpoint.recv_size= MAX_RECV_SIZE;
  115.     
  116.     //xu tioa zheng wei fen ci xie ru
  117.     if(write(fd,&breakpoint,sizeof(breakpoint)) != sizeof(breakpoint))
  118.     {
  119.         perror("ERR:Save_Breakpoint");
  120.         exit(1);
  121.     }
  122.     close(fd);
  123.     return 0;
  124. }
  125. /*
  126. 功能:保存文件,追加写
  127. 参数:
  128. 返回:
  129. 0---------成功
  130. */
  131. int Save_File(char *filebuf,int filelength,char *filename)
  132. {
  133.     int fd;
  134.     /* 创建目的文件追加写 */
  135.     if((fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR))==-1)
  136.     {
  137.         fprintf(stderr,"Open %s Error:%s\n",filename,strerror(errno));
  138.         exit(1);
  139.     }
  140.     //xu tioa zheng wei fen ci xie ru
  141.     if(write(fd,filebuf,filelength) != filelength)
  142.     {
  143.         perror("ERR:Save_File");
  144.         exit(1);
  145.     }
  146.     close(fd);
  147.     return 0;
  148. }
  149.  
  150. int HTTP_GetResponseCode(void)
  151. {
  152.  
  153.  
  154. }
  155.  /*
  156. 功能:读取http返回的协议实体主体长度
  157. 参数:
  158. revbuf--------接收到的返回值
  159. 返回值:
  160. >=0---------内容(实体主体)的长度
  161. -1-----------数据返回错误
  162. */
  163. int HTTP_GetRecvLength(char *revbuf)
  164. {
  165.     char *p1 = NULL;
  166.     int HTTP_Body = 0;//内容体长度
  167.     int HTTP_Head = 0;//HTTP 协议头长度
  168.     HTTP_Body = HTTP_GetContentLength(revbuf);
  169.     if(HTTP_Body == -1)
  170.         return -1;
  171.     p1=strstr(revbuf,"\r\n\r\n");
  172.     if(p1==NULL)
  173.         return -1;
  174.     else
  175.     {
  176.         HTTP_Head = p1- revbuf +4;// 4是\r\n\r\n的长度
  177.         return HTTP_Body+HTTP_Head;
  178.     }
  179. }
  180. /*
  181. 功能:读取http返回的Content-Length长度
  182. 参数:
  183. revbuf--------接收到的数据
  184. 返回值:
  185. >=0---------Content-Length长度
  186. -1-----------数据返回错误
  187. */
  188. int HTTP_GetContentLength(char *revbuf)
  189. {
  190.     char *p1 = NULL, *p2 = NULL;
  191.     int HTTP_Body = 0;//内容体长度
  192.     p1 = strstr(revbuf,"Content-Length");
  193.     if(p1 == NULL)
  194.         return -1;
  195.     else
  196.     {
  197.         p2 = p1+strlen("Content-Length")+ 2;
  198.         HTTP_Body = atoi(p2);
  199.         return HTTP_Body;
  200.     }
  201. }
  202.  /*
  203.  功能:
  204.  参数:
  205.  sockfd--------接收到的返回值
  206.  返回值:
  207.  >0---------接收到长度
  208.  -1----------失败
  209.  =0---------服务端断开连接
  210.  注:内部接收缓冲10k
  211.  */
  212. int HTTP_Recv(int sockfd,char *buf_recv)
  213. {
  214.     int ret;
  215.     int recvlen=0;
  216.     int downloadlen = 0;
  217.     //int contentlen=0;
  218.     char buf_recv_tmp[10*1024+1];
  219.     
  220.     memset(buf_recv_tmp,0x0,sizeof(buf_recv_tmp));
  221.     while(1)
  222.     {
  223.         ret = Recv(sockfd,buf_recv_tmp+recvlen,sizeof(buf_recv_tmp)-1,0);
  224.         if(ret <= 0)//下载失败
  225.         {
  226.             perror("ERR:recv fail");
  227.             return ret;
  228.         }
  229.     
  230.     
  231.         if(recvlen == 0)
  232.         {
  233.             #ifdef DEBUG_HTTP_RECV
  234.             printf("recv len = %d\n", ret);
  235.              printf("recv = %s\n", buf_recv_tmp);
  236.             #endif
  237.             //获取需要下载长度;
  238.             downloadlen = HTTP_GetRecvLength(buf_recv_tmp);
  239.             #ifdef DEBUG_HTTP_RECV
  240.             printf("downloadlen = %d\n",downloadlen);
  241.             #endif
  242.         }
  243.         recvlen += ret;
  244.         #ifdef DEBUG_HTTP_RECV
  245.         printf("total recvlen = %d\n",recvlen);
  246.         #endif
  247.         if(downloadlen == recvlen)//下载完成
  248.             break;
  249.     }
  250.     memcpy(buf_recv,buf_recv_tmp,downloadlen);
  251.     return recvlen;
  252. }
  253. /*
  254. 功能:获取下载url中的文件名,最后一个/后的字符
  255. 参数:
  256. 返回值:
  257. 0-----------成功
  258. -1----------失败
  259. :内部接收缓冲10k
  260. */
  261. int HTTP_GetFileName(char *url,char *filename)
  262. {
  263.     //提取url中最后一个/后的内容
  264.     int len;
  265.     int i;
  266.     len = strlen(url);
  267.     for(i=len-1;i>0;i--)
  268.     {
  269.         if(url[i] == '/')
  270.             break;
  271.     }
  272.     if(i == 0)//下载地址错误
  273.     {
  274.         printf("url not contain '/'\n");
  275.         return -1;
  276.     }
  277.     else
  278.     {
  279.     
  280.         strcpy(filename,url+i+1);
  281.         #ifdef DEBUG_HTTP
  282.         printf("filename=%s\n",filename);
  283.         #endif
  284.         return 0;
  285.     }
  286. }
  287. /*
  288. 功能:获取下载url中的路径,第一个/后的字符
  289. 参数:
  290. 返回值:
  291. 0-----------成功
  292. -1----------失败
  293. :url ex "http://host:port/path"
  294. */
  295. int HTTP_GetPath(char *url,char *path)
  296. {
  297.     char *p;
  298.     p = strstr(url,"http://");
  299.     if(p == NULL)
  300.     {
  301.         p = strchr(url,'/');
  302.         if(p == NULL)
  303.             return -1;
  304.         else
  305.         {
  306.             strcpy(path,p);
  307.             return 0;
  308.         }
  309.     }
  310.     else
  311.     {
  312.         p = strchr(url+strlen("http://"),'/');
  313.         if(p == NULL)
  314.             return -1;
  315.         else
  316.         {
  317.             strcpy(path,p);
  318.             return 0;
  319.         }
  320.     }
  321. }
  322. /*
  323. 功能:获取下载url中的ip和port,ip支持域名,端口默认为80
  324. 参数:
  325. 返回值:
  326. 1-----------域名式
  327. 2-----------ip port式
  328. -1----------失败
  329. :url ex "http://host:port/path"
  330. */
  331. int HTTP_Get_IP_PORT(char *url,char *ip,char *port)
  332. {
  333.     char *p = NULL;
  334.     int offset = 0;
  335.     char DOMAIN_NAME[128];
  336.     p = strstr(url,"http://");
  337.     if(p == NULL)
  338.     {
  339.         offset = 0;
  340.     }
  341.     else
  342.     {
  343.         offset = strlen("http://");
  344.     }
  345.     p = strchr(url+offset,'/');
  346.     if(p == NULL)
  347.     {
  348.         printf("url:%s format error\n",url);
  349.         return -1;
  350.         
  351.     }
  352.     else
  353.     {
  354.         memset(DOMAIN_NAME,0x0,sizeof(DOMAIN_NAME));
  355.         memcpy(DOMAIN_NAME,url+offset,(p-url-offset));
  356.         p = strchr(DOMAIN_NAME,':');
  357.         if(p == NULL)
  358.         {
  359.             strcpy(ip,DOMAIN_NAME);
  360.             strcpy(port,"80");
  361.             //printf("ip %p,port %p\n",ip,port);
  362.             
  363.             #ifdef DEBUG_HTTP
  364.             printf("ip=%s,port=%s\n",ip,port);//debug info
  365.             #endif
  366.             return 1;
  367.         }
  368.         else
  369.         {    
  370.             *p = '\0';
  371.             strcpy(ip,DOMAIN_NAME);
  372.             strcpy(port,p+1);
  373.             
  374.             #ifdef DEBUG_HTTP
  375.             printf("ip=%s,port=%s\n",ip,port);//debug info
  376.             #endif
  377.             return 2;
  378.         }
  379.         return 0;
  380.     }
  381.     
  382. }
  383. void Package_Url_Get_File(char *path, char *range)
  384. {
  385.     char buf[64];
  386.     memset(g_buf_send,0x0,sizeof(g_buf_send));        
  387.     sprintf(g_buf_send, "GET %s",path);
  388.     
  389.     //HTTP/1.1\r\n 前面需要一个空格
  390.     strcat(g_buf_send," HTTP/1.1\r\n");
  391.     strcat(g_buf_send, "Host: ");
  392.     strcat(g_buf_send, g_host);
  393.     //strcat(g_buf_send, ":");
  394.     //strcat(g_buf_send, PORT);
  395.     
  396.     sprintf(buf, "\r\nRange: bytes=%s",range);
  397.     strcat(g_buf_send,buf);
  398.     strcat(g_buf_send, "\r\nKeep-Alive: 200");
  399.     strcat(g_buf_send,"\r\nConnection: Keep-Alive\r\n\r\n");
  400.     
  401. }
  402. int Package_Url_Get_FileSize(char *url)
  403. {
  404.     
  405.     memset(g_buf_send,0x0,sizeof(g_buf_send));        
  406.     sprintf(g_buf_send, "HEAD %s",url);
  407.         //HTTP/1.1\r\n 前面需要一个空格
  408.     strcat(g_buf_send," HTTP/1.1\r\n");
  409.     strcat(g_buf_send, "Host: ");
  410.     strcat(g_buf_send, g_host);
  411.     //strcat(g_buf_send, ":");
  412.     //strcat(g_buf_send, PORT);
  413.     strcat(g_buf_send,"\r\nConnection: Keep-Alive\r\n\r\n");
  414.     return 0;
  415. }
  416. int HTTP_GetFileSize(int sockfd,char *path)
  417. {
  418.     int ret = -1;
  419.     char buf_recv_tmp[10*1024+1];
  420.     Package_Url_Get_FileSize(path);
  421. #ifdef DEBUG_HTTP
  422.     printf("send = %s \n",g_buf_send);
  423. #endif
  424.     Send(sockfd, g_buf_send, strlen(g_buf_send), 0);
  425.     memset(buf_recv_tmp,0x0,sizeof(buf_recv_tmp));                                                
  426.     ret = Recv(sockfd,buf_recv_tmp,sizeof(buf_recv_tmp)-1,0);
  427. #ifdef DEBUG_HTTP
  428.     printf("recv len = %d\n", ret);
  429.     printf("recv = %s\n", buf_recv_tmp);
  430. #endif
  431.     if(ret <= 0)
  432.     {
  433.         perror("ERR:recv fail GetFileSize()");
  434.         return -1;
  435.     }
  436.     ret = HTTP_GetContentLength(buf_recv_tmp);
  437.     if(ret <= 0)
  438.         return -1;
  439.     else
  440.         return ret;
  441. }
  442. /*
  443. 功能:分段下载文件
  444. 参数:
  445. 返回值:
  446. >0----------已下载文件大小(不包含上次下载)
  447. -1----------失败
  448. */
  449. int HTTP_GetFile(int sockfd,char *path,int filelength,int download_size,char *filebuf)
  450. {
  451.     int count;
  452.     char range[32];
  453.     int i;
  454.     int j = 0;//成功下载次数
  455.     int ret = -1;
  456.     char *p = NULL;
  457.     int download_index;//下载开始索引
  458.     count = (filelength%MAX_RECV_SIZE)?(filelength/MAX_RECV_SIZE +1):(filelength/MAX_RECV_SIZE);
  459.     download_index = download_size/MAX_RECV_SIZE;
  460.     for(i=download_index;i<count;i++)
  461.     {
  462.         //if(i == 20)//测试断点
  463.             //break;
  464.         if((i == (count-1))&&(filelength%MAX_RECV_SIZE))
  465.             sprintf(range,"%d-%d",i*MAX_RECV_SIZE,filelength-1);
  466.         else
  467.             sprintf(range,"%d-%d",i*MAX_RECV_SIZE,(i+1)*MAX_RECV_SIZE-1);
  468.         Package_Url_Get_File(path,range);
  469.         #ifdef DEBUG_HTTP
  470.          printf("send = %s \n",g_buf_send);
  471.         #endif
  472.          Send(sockfd, g_buf_send, strlen(g_buf_send), 0);
  473.         /*需改为提取http 返回协议头和协议体总长,然后定长接收*/
  474.         memset(g_buf_recv,0x0,sizeof(g_buf_recv));                                             
  475.         ret = HTTP_Recv(sockfd,g_buf_recv);
  476.         if(ret < 0)
  477.             break;
  478.         if(ret == 0 )//服务端断开连接
  479.         {
  480.             sockfd = Socket_Connect(g_ip,g_port);
  481.              i--;
  482.             continue;
  483.         }
  484.         /*提取协议体数据,保存在filebuf中*/
  485.         p = strstr(g_buf_recv,"\r\n\r\n");
  486.         if(p == NULL)//jia ru duan dian baocun
  487.         {
  488.             printf("ERR:g_buf_recv not contain end flag\n");
  489.             break;
  490.         }
  491.          else
  492.          {
  493.              memcpy(filebuf+j*MAX_RECV_SIZE,p+4,MAX_RECV_SIZE);
  494.              j++;
  495.          }
  496.     }
  497.     if(i == count)
  498.         return (filelength-download_size);
  499.     else
  500.         return (i*MAX_RECV_SIZE-download_size);
  501. }
  502. /*
  503. 功能:HTTP下载文件
  504. 参数:
  505. 返回值:
  506. 0----------下载完成
  507. -1---------失败
  508. -2---------部分下载完成
  509. :保存文件到bin所在目录
  510. */
  511. int HTTP_DownloadFile(char *url,char *save_path)
  512. {
  513.     int ret;
  514.     int sockfd;
  515.     int filesize;
  516.     int download_size;
  517.     char filename[FILENAME_LEN+1];
  518.     char filename_bp[FILENAME_LEN+3+1];
  519.     char *filebuf;
  520.     char save_file_path[FILENAME_LEN+1];//保存下载文件的路径+文件名
  521.     char path[PATH_LEN+1];//url中的path
  522.     //提取ip和port或url(url 暂不实现,需要gethostbyname linux)
  523.     ret = HTTP_Get_IP_PORT(url,g_ip,g_port);
  524.     if(ret == -1)
  525.         return -1;
  526.     else
  527.     {
  528.         sprintf(g_host,"%s:%s",g_ip,g_port);
  529.     }
  530.     //提取下载文件名
  531.     ret = HTTP_GetFileName(url,filename);
  532.     if(ret == -1)
  533.         return -1;
  534.     ret = HTTP_GetPath(url,path);
  535.     if(ret == -1)
  536.         return -1;
  537.     //sleep(3);//debug info
  538.     //建立连接
  539.     sockfd = Socket_Connect(g_ip,g_port);
  540.     //获取下载文件总大小
  541.     filesize = HTTP_GetFileSize(sockfd,path);
  542.     if(filesize == -1)
  543.         return -1;
  544.     //#ifdef DEBUG_HTTP
  545.     printf("http need download size %d\n",filesize);
  546.     //#endif
  547.     //malloc分配存储文件空间
  548.     filebuf = (char *)malloc(filesize);
  549.     if(filebuf == NULL)
  550.     {
  551.         perror("malloc filebuf fail");
  552.         return -1;
  553.     }
  554.     else
  555.         memset(filebuf,0x0,filesize);
  556.     download_size = Get_Breakpoint(url,filename,NULL);
  557.     #ifdef DEBUG_HTTP
  558.     printf("breakpoint download_size=%d\n",download_size);//debug info
  559.     sleep(3);//debug info
  560.     #endif
  561.     //分段下载文件
  562.     ret = HTTP_GetFile(sockfd,path,filesize,download_size,filebuf);
  563.     Close(sockfd);
  564.     if(ret < 0)
  565.     {
  566.         free(filebuf);
  567.         return -1;
  568.     }
  569.     else
  570.     {
  571.         sprintf(save_file_path,"%s%s",save_path,filename);
  572.         
  573.         #ifdef DEBUG_HTTP
  574.         printf("save_path=%s\n",save_path);
  575.         printf("filename=%s\n",filename);
  576.         printf("save_file_path=%s\n",save_file_path);
  577.         printf("download_size = %d\n",ret);
  578.         #endif
  579.         Save_File(filebuf,ret,save_file_path);
  580.         free(filebuf);
  581.         if((ret+download_size) == filesize)//全部下载完成
  582.         {
  583.             sprintf(filename_bp,"%s.bp",filename);
  584.             remove(filename_bp);
  585.             
  586.      printf("download success\n");
  587.             return 0;
  588.         }
  589.         else//部分下载完成
  590.         {
  591.             printf("part download success\n");
  592.             //保存断点信息
  593.             Save_Breakpoint(url,save_file_path,ret+download_size,NULL);
  594.             return -2;
  595.         }
  596.     }
  597. }

socket.

点击(此处)折叠或打开

  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(-1 == 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. }
posted @ 2020-08-16 19:59  专注it  阅读(2669)  评论(0编辑  收藏  举报