自己用C语言写的一个http下载工具

2014/10/1820:05:10 7

      今天在处理下载文件时,发现在两台服务器做双机环境下,备机需要同步主机配置文件,而这些配置文件存放在WEB目录中的某些目录,相对路径已知,发现用系统自带的wget工具下载配置文件时,必须要把文件完整下载过来后再去读配置文件,这样感觉有点不灵活,于是自己写了一个HTTP下载工具,这样在获取配置文件内容时比较灵活,可以无缝加载配置文件,而不需要重启服务,以下程序摘录自代码中的完整示例代码,示例代码没有做什么异常处理,大家如果使用的话可以把它更加完善一下,http_client.c全文如下:

/**************************************************************
 *     Copyright (C) 2014-2014 All rights reserved.
 *       @Version: 1.0
 *       @Created: 2014-10-18 19:31:30
 *        @Author: guozi - 411239339@qq.com
 *   @Description: http客户端程序,用于向WEB服务器发起http请求,将返回
 *   结果写入文件
 *       @History: 
 **************************************************************/
#include   <stdio.h>
#include   <stdlib.h>
#include   <string.h>
#include   <sys/socket.h>
#include   <errno.h>
#include   <unistd.h>
#include   <netinet/in.h>
#include   <limits.h>
#include   <netdb.h>
#include   <arpa/inet.h>
#include   <ctype.h>
#include   <sys/types.h>
#include   <sys/stat.h>
#include   <fcntl.h>
FILE *gFile;
/**
 * @brief http_string_strchr 搜索字符串右边起的第一个匹配字符
 *
 * @Param: s  原始字符串
 * @Param: x  待匹配的字符
 *
 * Returns: 索到即返回x对应的值,否则返回0
 */
char* http_string_strchr(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;
}
/**
 * @brief http_string_lower 字符串转换为全小写 
 *
 * @Param: s  原始字符串
 */
void   http_string_lower(char* s)
{
    char *p = s;
    while(*p && *p!='\0' )
    {
        if(*p > 'A' && *p < 'Z')
            *p = *p + 32;
        p++;
    }
}
/**
 * @brief http_gethost_info 字符串src中分析出网站地址和端口,并得到用户要下载的文件
 *
 * @Param: src  输入字符串
 * @Param: web  WEB地址
 * @Param: file  需要下载的文件
 * @Param: port  WEB端口号,默认为80
 */
void http_gethost_info(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;
    }
}
/**
 * @brief http_open_file 
 *
 * @Param: file_path
 *
 * Returns: 
 */
FILE *http_open_file(char *file_path)
{
    gFile = fopen(file_path,"a+");
    return gFile;
}
/**
 * @brief http_file_exsits 判断文件是否存在
 *
 * @Param: path 文件路径
 *
 * Returns: 存在返回true,不存在返回false
 */
int http_file_exsits(const char *path)
{
    struct stat statbuf;
    if(lstat(path, &statbuf) ==0)
        return S_ISREG(statbuf.st_mode) != 0;//判断文件是否为常规文件
    return 0;
}
/**
 * @brief http_socket_init 初始化套接字
 *
 * @Param: port
 * @Param: host_addr
 *
 * Returns: 返回套接字描述符
 */
int http_socket_init(int port, char *host_addr)
{
    struct sockaddr_in   server_addr;
    struct hostent   *host;
    int sockfd;
    if((host=gethostbyname(host_addr)) == NULL)/*取得主机IP地址*/
    {
        fprintf(stderr, "Gethostname   error,   %s\n ",   strerror(errno));
        return -1;
    }
    /*   客户程序开始建立   sockfd描述符   */
    if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1)/*建立SOCKET连接*/
    {
        fprintf(stderr, "Socket   Error:%s\a\n ",strerror(errno));
        return -1;
    }
    /*   客户程序填充服务端的资料   */
    bzero(&server_addr,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);
    /*   客户程序发起连接请求   */
    if(connect(sockfd, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr)) == -1)/*连接网站*/
    {
        fprintf(stderr, "Connect   Error:%s\a\n ",strerror(errno));
        return -1;
    }
    return sockfd;
}
/********************************************************************* 
 *filename:   httpclient.c 
 *purpose:   HTTP协议客户端程序,可以用来下载网页 
 *********************************************************************/
int   main(int   argc,   char   *argv[])
{
    int sockfd = 0;
    char buffer[1024] = "";
    int port = 0;
    int nbytes = 0;
    char host_file[1024] = "";
    char host_addr[256] = "";
    char request[1024] = "";
    int send = 0;
    int totalsend = 0;
    int i = 0;
    FILE *file_fd;
    char *pt;
    char psave[4096];
    char *file_name;
    size_t index = 0;
    if(argc < 2)
    {
        fprintf(stderr, "Usage:%s   [web-address]   \n ",argv[0]);
        exit(1);
    }
    printf( "parameter.1 is: %s\n ", argv[1]);
    http_gethost_info(argv[1], host_addr, host_file, &port);/*分析网址、端口、文件名等*/
    printf( "webhost:%s\n ", host_addr);
    printf( "hostfile:%s\n ", host_file);
    printf( "port:%d\n\n ", port);
    file_name = argv[2];
    if(! file_name)
        file_name = host_file;
    sockfd = http_socket_init(port, host_addr);
    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:   Close\r\n\r\n ", host_file, host_addr, port);
    printf( "%s\n", request);/*准备request,将要发送给主机*/
    /*取得真实的文件名*/
    if(*host_file)
    {
        pt = http_string_strchr(host_file, '/');
    }
    else
    {
        pt = 0;
    }
    /*发送http请求request*/
    send = 0;
    totalsend = 0;
    nbytes=strlen(request);
    while(totalsend < nbytes)
    {
        send = write(sockfd, request+totalsend, nbytes-totalsend);
        if(send == -1)
        {
            printf( "send error!%s\n ", strerror(errno));
            exit(0);
        }
        totalsend += send;
        printf("%d bytes send OK!\n ", totalsend);
    }
    /*请求结束,等待响应*/
    /*将结果写回到文件*/
    if(http_file_exsits(file_name) )
    {
        remove(file_name);
    }
    file_fd = http_open_file(file_name);
    if(file_fd == NULL)
    {
        perror("Open file error!\n");
        return -1;
    }
    i=0;
    /*连接成功了,接收http响应,每次处理4096个字节*/
    memset(psave,0,4096);
    while((nbytes=read(sockfd,buffer,1))==1)
    {
        if(i < 4)
        {
            /*这里处理http头部*/
            if(buffer[0] == '\r' || buffer[0] == '\n')
            {
                i++;
            }
            else
            {
                i = 0;
            }
            printf( "%c", buffer[0]);/*把http头信息打印在屏幕上*/
        }
        else /*如果结尾部分不为\r\n\r\n则表示头接收完毕,下面是请求内容*/
        {
            psave[index++] = buffer[0];
            if(index > 4096)
            {
                fwrite(psave,4096,1,file_fd);
                fflush(file_fd);
                memset(psave,0,4096);
                index = 0;
            }
        }
    }
    /*将剩余的字符写入文件*/
    if(index <= 4096)
    {
        fwrite(psave,index,1,file_fd);
        fflush(file_fd);
    }
    close(sockfd);
    fclose(file_fd);
    return 0;
}

编译:gcc http_client.c -o http_client

运行:./http_client  http://192.168.1.1/sql.conf  

也可以这样运行./http_client  192.168.1.1/sql.conf   /tmp/sql.conf  后面的参数为指定文件存储位置。

这样就会把远程服务器上的文件下载过来了。

  • 微信扫码赞助
  • weinxin
  • 支付宝赞助
  • weinxin

发表评论

您必须才能发表评论!

目前评论:7   其中:访客  0   博主  0

    • 果子

      感谢指正,写这个的目的当时只是为了练习,没有考虑到产品级别的代码

      • 周东

        续上一条
        gethostbyname()确实不能跟随端口号,在作者的c程序中,没有去掉,
        手动修改在代码调用gethostbyname()之前,把host_addr字符串的内容修改,去掉端口数据就可以下载了。
        还有一点小问题:下载的MP3
        文件播放的时候断断续续。

        • 周东

          http://210.38.1.143:9999/file.kuyinyun.com/group1/M00/52/54/rBBGdFO10gGAV2YSABeMNNnn7Sc042.mp3
          解释失败,看起来好像是Gethostname 失败了,带端口的不能使用gethostbyname()函数吗,求解

          • Soon

            Hello there, simply changed into alert to your weblog through Google, and found that it's truly informative.

            I am going to watch out for brussels. I'll be grateful in the event you continue this in future.

            Lots of people shall be benefited from your writing. Cheers!

            • 自学去

              写得不错,转载了 www.zixue7.com

              • lintao1984

                求在线客服源代码哟..谢谢

                • 生活需要深度

                  太给力了,期待果子能够跟新更多使用的东西!